mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Sol->Yul] Adding cleanUpArrayEnd util function.
Co-authored-by: chriseth <chris@ethereum.org> Co-authored-by: Alex Beregszaszi <alex@rtfs.hu> Co-authored-by: Kamil Śliwak <kamil.sliwak@codepoets.it> Co-authored-by: Alex Beregszaszi <alex@rtfs.hu> Co-authored-by: Kamil Śliwak <kamil.sliwak@codepoets.it>
This commit is contained in:
parent
1d1175c292
commit
3e5f5fccf9
@ -1168,21 +1168,7 @@ std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type)
|
||||
</isDynamic>
|
||||
|
||||
<?needsClearing>
|
||||
// Size was reduced, clear end of array
|
||||
if lt(newLen, oldLen) {
|
||||
let oldSlotCount := <convertToSize>(oldLen)
|
||||
let newSlotCount := <convertToSize>(newLen)
|
||||
let arrayDataStart := <dataPosition>(array)
|
||||
let deleteStart := add(arrayDataStart, newSlotCount)
|
||||
let deleteEnd := add(arrayDataStart, oldSlotCount)
|
||||
<?packed>
|
||||
// if we are dealing with packed array and offset is greater than zero
|
||||
// we have to partially clear last slot that is still used, so decreasing start by one
|
||||
let offset := mul(mod(newLen, <itemsPerSlot>), <storageBytes>)
|
||||
if gt(offset, 0) { <partialClearStorageSlot>(sub(deleteStart, 1), offset) }
|
||||
</packed>
|
||||
<clearStorageRange>(deleteStart, deleteEnd)
|
||||
}
|
||||
<cleanUpArrayEnd>(array, oldLen, newLen)
|
||||
</needsClearing>
|
||||
})");
|
||||
templ("functionName", functionName);
|
||||
@ -1193,19 +1179,49 @@ std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type)
|
||||
bool isMappingBase = _type.baseType()->category() == Type::Category::Mapping;
|
||||
templ("needsClearing", !isMappingBase);
|
||||
if (!isMappingBase)
|
||||
{
|
||||
templ("convertToSize", arrayConvertLengthToSize(_type));
|
||||
templ("dataPosition", arrayDataAreaFunction(_type));
|
||||
templ("clearStorageRange", clearStorageRangeFunction(*_type.baseType()));
|
||||
templ("packed", _type.baseType()->storageBytes() <= 16);
|
||||
templ("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes()));
|
||||
templ("storageBytes", to_string(_type.baseType()->storageBytes()));
|
||||
templ("partialClearStorageSlot", partialClearStorageSlotFunction());
|
||||
}
|
||||
templ("cleanUpArrayEnd", cleanUpStorageArrayEndFunction(_type));
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _type)
|
||||
{
|
||||
solAssert(_type.location() == DataLocation::Storage, "");
|
||||
solAssert(_type.baseType()->category() != Type::Category::Mapping, "");
|
||||
solAssert(!_type.isByteArray(), "");
|
||||
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "");
|
||||
|
||||
string functionName = "cleanup_storage_array_end_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) {
|
||||
_args = {"array", "len", "startIndex"};
|
||||
return Whiskers(R"(
|
||||
if lt(startIndex, len) {
|
||||
// Size was reduced, clear end of array
|
||||
let oldSlotCount := <convertToSize>(len)
|
||||
let newSlotCount := <convertToSize>(startIndex)
|
||||
let arrayDataStart := <dataPosition>(array)
|
||||
let deleteStart := add(arrayDataStart, newSlotCount)
|
||||
let deleteEnd := add(arrayDataStart, oldSlotCount)
|
||||
<?packed>
|
||||
// if we are dealing with packed array and offset is greater than zero
|
||||
// we have to partially clear last slot that is still used, so decreasing start by one
|
||||
let offset := mul(mod(startIndex, <itemsPerSlot>), <storageBytes>)
|
||||
if gt(offset, 0) { <partialClearStorageSlot>(sub(deleteStart, 1), offset) }
|
||||
</packed>
|
||||
<clearStorageRange>(deleteStart, deleteEnd)
|
||||
}
|
||||
)")
|
||||
("convertToSize", arrayConvertLengthToSize(_type))
|
||||
("dataPosition", arrayDataAreaFunction(_type))
|
||||
("clearStorageRange", clearStorageRangeFunction(*_type.baseType()))
|
||||
("packed", _type.baseType()->storageBytes() <= 16)
|
||||
("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes()))
|
||||
("storageBytes", to_string(_type.baseType()->storageBytes()))
|
||||
("partialClearStorageSlot", partialClearStorageSlotFunction())
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type)
|
||||
{
|
||||
string functionName = "resize_array_" + _type.identifier();
|
||||
@ -1230,6 +1246,30 @@ string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type)
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type)
|
||||
{
|
||||
solAssert(_type.isByteArray(), "");
|
||||
solAssert(_type.isDynamicallySized(), "");
|
||||
|
||||
string functionName = "clean_up_bytearray_end_slots_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) {
|
||||
_args = {"array", "len", "startIndex"};
|
||||
return Whiskers(R"(
|
||||
if gt(len, 31) {
|
||||
let dataArea := <dataLocation>(array)
|
||||
let deleteStart := add(dataArea, div(<roundUp>(startIndex), 32))
|
||||
// If we are clearing array to be short byte array, we want to clear only data starting from array data area.
|
||||
if lt(startIndex, 32) { deleteStart := dataArea }
|
||||
<clearStorageRange>(deleteStart, add(dataArea, div(add(len, 31), 32)))
|
||||
}
|
||||
)")
|
||||
("dataLocation", arrayDataAreaFunction(_type))
|
||||
("roundUp", roundUpFunction())
|
||||
("clearStorageRange", clearStorageRangeFunction(*_type.baseType()))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _type)
|
||||
{
|
||||
string functionName = "byte_array_decrease_size_" + _type.identifier();
|
||||
@ -1805,28 +1845,19 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
|
||||
|
||||
let oldLen := <byteArrayLength>(sload(slot))
|
||||
|
||||
// potentially truncate data
|
||||
<cleanUpEndArray>(slot, oldLen, newLen)
|
||||
|
||||
let srcOffset := 0
|
||||
<?fromMemory>
|
||||
srcOffset := 0x20
|
||||
</fromMemory>
|
||||
|
||||
// This is not needed in all branches.
|
||||
let dstDataArea
|
||||
if or(gt(oldLen, 31), gt(newLen, 31)) {
|
||||
dstDataArea := <dstDataLocation>(slot)
|
||||
}
|
||||
|
||||
if gt(oldLen, 31) {
|
||||
// potentially truncate data
|
||||
let deleteStart := add(dstDataArea, div(add(newLen, 31), 32))
|
||||
if lt(newLen, 32) { deleteStart := dstDataArea }
|
||||
<clearStorageRange>(deleteStart, add(dstDataArea, div(add(oldLen, 31), 32)))
|
||||
}
|
||||
switch gt(newLen, 31)
|
||||
case 1 {
|
||||
let loopEnd := and(newLen, not(0x1f))
|
||||
<?fromStorage> src := <srcDataLocation>(src) </fromStorage>
|
||||
let dstPtr := dstDataArea
|
||||
let dstPtr := <dstDataLocation>(slot)
|
||||
let i := 0
|
||||
for { } lt(i, loopEnd) { i := add(i, 0x20) } {
|
||||
sstore(dstPtr, <read>(add(src, srcOffset)))
|
||||
@ -1860,7 +1891,7 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
|
||||
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
||||
if (fromStorage)
|
||||
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
|
||||
templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType()));
|
||||
templ("cleanUpEndArray", cleanUpDynamicByteArrayEndSlotsFunction(_toType));
|
||||
templ("srcIncrement", to_string(fromStorage ? 1 : 0x20));
|
||||
templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload");
|
||||
templ("maskBytes", maskBytesFunctionDynamic());
|
||||
|
@ -207,6 +207,11 @@ public:
|
||||
/// signature: (array, newLen)
|
||||
std::string resizeArrayFunction(ArrayType const& _type);
|
||||
|
||||
/// @returns the name of a function that zeroes all storage array elements from `startIndex` to `len` (excluding).
|
||||
/// Assumes that `len` is the array length. Does nothing if `startIndex >= len`. Does not modify the stored length.
|
||||
/// signature: (array, len, startIndex)
|
||||
std::string cleanUpStorageArrayEndFunction(ArrayType const& _type);
|
||||
|
||||
/// @returns the name of a function that reduces the size of a storage array by one element
|
||||
/// signature: (array)
|
||||
std::string storageArrayPopFunction(ArrayType const& _type);
|
||||
@ -543,6 +548,14 @@ private:
|
||||
/// signature: (array, newLen)
|
||||
std::string resizeDynamicByteArrayFunction(ArrayType const& _type);
|
||||
|
||||
/// @returns the name of a function that cleans up elements of a storage byte array starting from startIndex.
|
||||
/// It will not copy elements in case of transformation to short byte array, and will not change array length.
|
||||
/// In case of startIndex is greater than len, doesn't do anything.
|
||||
/// In case of short byte array (< 32 bytes) doesn't do anything.
|
||||
/// If the first slot to be cleaned up is partially occupied, does not touch it. Cleans up only completely unused slots.
|
||||
/// signature: (array, len, startIndex)
|
||||
std::string cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type);
|
||||
|
||||
/// @returns the name of a function that increases size of byte array
|
||||
/// when we resize byte array frextractUsedSetLenom < 32 elements to >= 32 elements or we push to byte array of size 31 copying of data will occur
|
||||
/// signature: (array, data, oldLen, newLen)
|
||||
|
Loading…
Reference in New Issue
Block a user