Merge pull request #9905 from ethereum/deleteArrayPackedSol2Yul

[Sol->Yul] Implementing deleting of storage array of types that are packed in storage
This commit is contained in:
chriseth 2020-10-01 16:50:57 +02:00 committed by GitHub
commit e5771efdf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 3 deletions

View File

@ -873,6 +873,12 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
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)
}
})")
@ -883,6 +889,10 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
("dataPosition", arrayDataAreaFunction(_type))
("clearStorageRange", clearStorageRangeFunction(*_type.baseType()))
("maxArrayLength", (u256(1) << 64).str())
("packed", _type.baseType()->storageBytes() <= 16)
("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes()))
("storageBytes", to_string(_type.baseType()->storageBytes()))
("partialClearStorageSlot", partialClearStorageSlotFunction())
.render();
});
}
@ -1060,11 +1070,29 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type)
});
}
string YulUtilFunctions::partialClearStorageSlotFunction()
{
string functionName = "partial_clear_storage_slot";
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(slot, offset) {
let mask := <shr>(mul(8, sub(32, offset)), <ones>)
sstore(slot, and(mask, sload(slot)))
}
)")
("functionName", functionName)
("ones", formatNumber((bigint(1) << 256) - 1))
("shr", shiftRightFunctionDynamic())
.render();
});
}
string YulUtilFunctions::clearStorageRangeFunction(Type const& _type)
{
string functionName = "clear_storage_range_" + _type.identifier();
if (_type.storageBytes() < 32)
solAssert(_type.isValueType(), "");
solAssert(_type.storageBytes() >= 32, "Expected smaller value for storage bytes");
string functionName = "clear_storage_range_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
@ -1076,7 +1104,7 @@ string YulUtilFunctions::clearStorageRangeFunction(Type const& _type)
}
)")
("functionName", functionName)
("setToZero", storageSetToZeroFunction(_type))
("setToZero", storageSetToZeroFunction(_type.storageBytes() < 32 ? *TypeProvider::uint256() : _type))
("increment", _type.storageSize().str())
.render();
});

View File

@ -422,6 +422,11 @@ private:
/// @returns a function that reads a reference type from storage to memory (performing a deep copy).
std::string readFromStorageReferenceType(Type const& _type);
/// @returns the name of a function that will clear given storage slot
/// starting with given offset until the end of the slot
/// signature: (slot, offset)
std::string partialClearStorageSlotFunction();
langutil::EVMVersion m_evmVersion;
RevertStrings m_revertStrings;
MultiUseYulFunctionCollector& m_functionCollector;

View File

@ -0,0 +1,18 @@
contract C {
uint120[] data;
function f() public returns (uint120, uint120, uint120) {
data.push(123);
data.push(234);
data.push(345);
delete data;
assembly {
sstore(data.slot, 3)
}
return (data[0], data[1], data[2]);
}
}
// ====
// compileViaYul: also
// ----
// f() -> 0, 0, 0