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>
|
</isDynamic>
|
||||||
|
|
||||||
<?needsClearing>
|
<?needsClearing>
|
||||||
// Size was reduced, clear end of array
|
<cleanUpArrayEnd>(array, oldLen, newLen)
|
||||||
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)
|
|
||||||
}
|
|
||||||
</needsClearing>
|
</needsClearing>
|
||||||
})");
|
})");
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
@ -1193,19 +1179,49 @@ std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type)
|
|||||||
bool isMappingBase = _type.baseType()->category() == Type::Category::Mapping;
|
bool isMappingBase = _type.baseType()->category() == Type::Category::Mapping;
|
||||||
templ("needsClearing", !isMappingBase);
|
templ("needsClearing", !isMappingBase);
|
||||||
if (!isMappingBase)
|
if (!isMappingBase)
|
||||||
{
|
templ("cleanUpArrayEnd", cleanUpStorageArrayEndFunction(_type));
|
||||||
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());
|
|
||||||
}
|
|
||||||
return templ.render();
|
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 YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
string functionName = "resize_array_" + _type.identifier();
|
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 YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
string functionName = "byte_array_decrease_size_" + _type.identifier();
|
string functionName = "byte_array_decrease_size_" + _type.identifier();
|
||||||
@ -1805,28 +1845,19 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
|
|||||||
|
|
||||||
let oldLen := <byteArrayLength>(sload(slot))
|
let oldLen := <byteArrayLength>(sload(slot))
|
||||||
|
|
||||||
|
// potentially truncate data
|
||||||
|
<cleanUpEndArray>(slot, oldLen, newLen)
|
||||||
|
|
||||||
let srcOffset := 0
|
let srcOffset := 0
|
||||||
<?fromMemory>
|
<?fromMemory>
|
||||||
srcOffset := 0x20
|
srcOffset := 0x20
|
||||||
</fromMemory>
|
</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)
|
switch gt(newLen, 31)
|
||||||
case 1 {
|
case 1 {
|
||||||
let loopEnd := and(newLen, not(0x1f))
|
let loopEnd := and(newLen, not(0x1f))
|
||||||
<?fromStorage> src := <srcDataLocation>(src) </fromStorage>
|
<?fromStorage> src := <srcDataLocation>(src) </fromStorage>
|
||||||
let dstPtr := dstDataArea
|
let dstPtr := <dstDataLocation>(slot)
|
||||||
let i := 0
|
let i := 0
|
||||||
for { } lt(i, loopEnd) { i := add(i, 0x20) } {
|
for { } lt(i, loopEnd) { i := add(i, 0x20) } {
|
||||||
sstore(dstPtr, <read>(add(src, srcOffset)))
|
sstore(dstPtr, <read>(add(src, srcOffset)))
|
||||||
@ -1860,7 +1891,7 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
|
|||||||
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
||||||
if (fromStorage)
|
if (fromStorage)
|
||||||
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
|
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
|
||||||
templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType()));
|
templ("cleanUpEndArray", cleanUpDynamicByteArrayEndSlotsFunction(_toType));
|
||||||
templ("srcIncrement", to_string(fromStorage ? 1 : 0x20));
|
templ("srcIncrement", to_string(fromStorage ? 1 : 0x20));
|
||||||
templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload");
|
templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload");
|
||||||
templ("maskBytes", maskBytesFunctionDynamic());
|
templ("maskBytes", maskBytesFunctionDynamic());
|
||||||
|
@ -207,6 +207,11 @@ public:
|
|||||||
/// signature: (array, newLen)
|
/// signature: (array, newLen)
|
||||||
std::string resizeArrayFunction(ArrayType const& _type);
|
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
|
/// @returns the name of a function that reduces the size of a storage array by one element
|
||||||
/// signature: (array)
|
/// signature: (array)
|
||||||
std::string storageArrayPopFunction(ArrayType const& _type);
|
std::string storageArrayPopFunction(ArrayType const& _type);
|
||||||
@ -543,6 +548,14 @@ private:
|
|||||||
/// signature: (array, newLen)
|
/// signature: (array, newLen)
|
||||||
std::string resizeDynamicByteArrayFunction(ArrayType const& _type);
|
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
|
/// @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
|
/// 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)
|
/// signature: (array, data, oldLen, newLen)
|
||||||
|
Loading…
Reference in New Issue
Block a user