mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Sol->Yul] Optimizing delete struct.
Co-authored-by: Daniel Kirchner <daniel@ekpyron.org>
This commit is contained in:
parent
92a2cddbfa
commit
6f7947cfa5
@ -4,3 +4,32 @@ Solidity IR-based Codegen Changes
|
|||||||
|
|
||||||
This section highlights the main differences between the old and the IR-based codegen,
|
This section highlights the main differences between the old and the IR-based codegen,
|
||||||
along with the reasoning behind the changes and how to update affected code.
|
along with the reasoning behind the changes and how to update affected code.
|
||||||
|
|
||||||
|
Semantic Only Changes
|
||||||
|
=====================
|
||||||
|
|
||||||
|
This section lists the changes that are semantic-only, thus potentially
|
||||||
|
hiding new and different behavior in existing code.
|
||||||
|
|
||||||
|
* When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched.
|
||||||
|
Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past).
|
||||||
|
|
||||||
|
::
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >0.7.0;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint64 y;
|
||||||
|
uint64 z;
|
||||||
|
}
|
||||||
|
S s;
|
||||||
|
function f() public {
|
||||||
|
// ...
|
||||||
|
delete s;
|
||||||
|
// s occupies only first 16 bytes of the 32 bytes slot
|
||||||
|
// delete will write zero to the full slot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
We have the same behavior for implicit delete, for example when array of structs is shortened.
|
||||||
|
@ -1266,19 +1266,31 @@ string YulUtilFunctions::clearStorageStructFunction(StructType const& _type)
|
|||||||
MemberList::MemberMap structMembers = _type.nativeMembers(nullptr);
|
MemberList::MemberMap structMembers = _type.nativeMembers(nullptr);
|
||||||
vector<map<string, string>> memberSetValues;
|
vector<map<string, string>> memberSetValues;
|
||||||
|
|
||||||
|
set<u256> slotsCleared;
|
||||||
for (auto const& member: structMembers)
|
for (auto const& member: structMembers)
|
||||||
{
|
if (member.type->storageBytes() < 32)
|
||||||
auto const& [memberSlotDiff, memberStorageOffset] = _type.storageOffsetsOfMember(member.name);
|
{
|
||||||
|
auto const& slotDiff = _type.storageOffsetsOfMember(member.name).first;
|
||||||
|
if (!slotsCleared.count(slotDiff))
|
||||||
|
{
|
||||||
|
memberSetValues.emplace_back().emplace("clearMember", "sstore(add(slot, " + slotDiff.str() + "), 0)");
|
||||||
|
slotsCleared.emplace(slotDiff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto const& [memberSlotDiff, memberStorageOffset] = _type.storageOffsetsOfMember(member.name);
|
||||||
|
solAssert(memberStorageOffset == 0, "");
|
||||||
|
|
||||||
memberSetValues.emplace_back().emplace("clearMember", Whiskers(R"(
|
memberSetValues.emplace_back().emplace("clearMember", Whiskers(R"(
|
||||||
<setZero>(add(slot, <memberSlotDiff>), <memberStorageOffset>)
|
<setZero>(add(slot, <memberSlotDiff>), <memberStorageOffset>)
|
||||||
)")
|
)")
|
||||||
("setZero", storageSetToZeroFunction(*member.type))
|
("setZero", storageSetToZeroFunction(*member.type))
|
||||||
("memberSlotDiff", memberSlotDiff.str())
|
("memberSlotDiff", memberSlotDiff.str())
|
||||||
("memberStorageOffset", to_string(memberStorageOffset))
|
("memberStorageOffset", to_string(memberStorageOffset))
|
||||||
.render()
|
.render()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(slot) {
|
function <functionName>(slot) {
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint32 a;
|
||||||
|
S[] x;
|
||||||
|
}
|
||||||
|
S s;
|
||||||
|
function f() public returns (uint256 r1, uint256 r2, uint256 r3) {
|
||||||
|
assembly {
|
||||||
|
// 2 ** 150 - 1
|
||||||
|
sstore(s.slot, 1427247692705959881058285969449495136382746623)
|
||||||
|
}
|
||||||
|
s.a = 1;
|
||||||
|
s.x.push(); s.x.push();
|
||||||
|
S storage ptr1 = s.x[0];
|
||||||
|
S storage ptr2 = s.x[1];
|
||||||
|
assembly {
|
||||||
|
// 2 ** 150 - 1
|
||||||
|
sstore(ptr1.slot, 1427247692705959881058285969449495136382746623)
|
||||||
|
sstore(ptr2.slot, 1427247692705959881058285969449495136382746623)
|
||||||
|
}
|
||||||
|
s.x[0].a = 2; s.x[1].a = 3;
|
||||||
|
delete s;
|
||||||
|
assert(s.a == 0);
|
||||||
|
assert(s.x.length == 0);
|
||||||
|
assembly {
|
||||||
|
r1 := sload(s.slot)
|
||||||
|
r2 := sload(ptr1.slot)
|
||||||
|
r3 := sload(ptr2.slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f() -> 0, 0, 0
|
@ -0,0 +1,24 @@
|
|||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint64 y;
|
||||||
|
uint64 z;
|
||||||
|
}
|
||||||
|
S s;
|
||||||
|
function f() public returns (uint256 ret) {
|
||||||
|
assembly {
|
||||||
|
// 2 ** 150 - 1
|
||||||
|
sstore(s.slot, 1427247692705959881058285969449495136382746623)
|
||||||
|
}
|
||||||
|
s.y = 1; s.z = 2;
|
||||||
|
delete s;
|
||||||
|
assert(s.y == 0);
|
||||||
|
assert(s.z == 0);
|
||||||
|
assembly {
|
||||||
|
ret := sload(s.slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f() -> 0
|
@ -0,0 +1,29 @@
|
|||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint32 a;
|
||||||
|
uint32[3] b;
|
||||||
|
uint32[] x;
|
||||||
|
}
|
||||||
|
S s;
|
||||||
|
function f() public returns (uint256 ret) {
|
||||||
|
assembly {
|
||||||
|
// 2 ** 150 - 1
|
||||||
|
sstore(s.slot, 1427247692705959881058285969449495136382746623)
|
||||||
|
}
|
||||||
|
s.a = 1;
|
||||||
|
s.b[0] = 2; s.b[1] = 3;
|
||||||
|
s.x.push(4); s.x.push(5);
|
||||||
|
delete s;
|
||||||
|
assert(s.a == 0);
|
||||||
|
assert(s.b[0] == 0);
|
||||||
|
assert(s.b[1] == 0);
|
||||||
|
assert(s.x.length == 0);
|
||||||
|
assembly {
|
||||||
|
ret := sload(s.slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f() -> 0
|
Loading…
Reference in New Issue
Block a user