Copying structs from storage to storage.

This commit is contained in:
chriseth 2020-11-12 15:23:43 +01:00
parent 1911a5a25a
commit 98cc7a1ea9
5 changed files with 69 additions and 49 deletions

View File

@ -2330,10 +2330,6 @@ string YulUtilFunctions::updateStorageValueFunction(
{ {
solAssert(_toType.category() == Type::Category::Struct, ""); solAssert(_toType.category() == Type::Category::Struct, "");
solUnimplementedAssert(
fromReferenceType->location() != DataLocation::Storage,
"Copying from storage to storage is not yet implemented."
);
auto const& fromStructType = dynamic_cast<StructType const&>(_fromType); auto const& fromStructType = dynamic_cast<StructType const&>(_fromType);
auto const& toStructType = dynamic_cast<StructType const&>(_toType); auto const& toStructType = dynamic_cast<StructType const&>(_toType);
solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
@ -2341,6 +2337,7 @@ string YulUtilFunctions::updateStorageValueFunction(
Whiskers templ(R"( Whiskers templ(R"(
function <functionName>(slot, value) { function <functionName>(slot, value) {
<?fromStorage> if eq(slot, value) { leave } </fromStorage>
<#member> <#member>
{ {
<updateMemberCall> <updateMemberCall>
@ -2348,6 +2345,7 @@ string YulUtilFunctions::updateStorageValueFunction(
</member> </member>
} }
)"); )");
templ("fromStorage", fromStructType.dataStoredIn(DataLocation::Storage));
templ("functionName", functionName); templ("functionName", functionName);
MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr); MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr);
@ -2356,67 +2354,81 @@ string YulUtilFunctions::updateStorageValueFunction(
vector<map<string, string>> memberParams(structMembers.size()); vector<map<string, string>> memberParams(structMembers.size());
for (size_t i = 0; i < structMembers.size(); ++i) for (size_t i = 0; i < structMembers.size(); ++i)
{ {
solAssert(structMembers[i].type->memoryHeadSize() == 32, ""); Type const& memberType = *structMembers[i].type;
bool fromCalldata = fromStructType.location() == DataLocation::CallData; solAssert(memberType.memoryHeadSize() == 32, "");
auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name); auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name);
Whiskers t(R"( Whiskers t(R"(
let memberSlot := add(slot, <memberStorageSlotDiff>) let memberSlot := add(slot, <memberStorageSlotDiff>)
let memberSrcPtr := add(value, <memberOffset>)
<?fromCalldata> <?fromCalldata>
let <memberValues> :=
<?dynamicallyEncodedMember> <?dynamicallyEncodedMember>
let <memberCalldataOffset> := <accessCalldataTail>(value, add(value, <memberOffset>)) <accessCalldataTail>(value, memberSrcPtr)
<!dynamicallyEncodedMember> <!dynamicallyEncodedMember>
let <memberCalldataOffset> := add(value, <memberOffset>) memberSrcPtr
</dynamicallyEncodedMember> </dynamicallyEncodedMember>
<?isValueType> <?isValueType>
let <memberValues> := <loadFromMemoryOrCalldata>(<memberCalldataOffset>) <memberValues> := <read>(<memberValues>)
<updateMember>(memberSlot, <memberStorageOffset>, <memberValues>)
<!isValueType>
<updateMember>(memberSlot, <memberCalldataOffset>)
</isValueType> </isValueType>
<!fromCalldata>
let memberMemoryOffset := add(value, <memberOffset>)
let <memberValues> := <loadFromMemoryOrCalldata>(memberMemoryOffset)
<updateMember>(memberSlot, <?hasOffset><memberStorageOffset>,</hasOffset> <memberValues>)
</fromCalldata> </fromCalldata>
<?fromMemory>
let <memberValues> := <read>(memberSrcPtr)
</fromMemory>
<?fromStorage>
let <memberValues> :=
<?isValueType>
<read>(memberSrcPtr)
<!isValueType>
memberSrcPtr
</isValueType>
</fromStorage>
<updateStorageValue>(memberSlot, <memberValues>)
)"); )");
bool fromCalldata = fromStructType.location() == DataLocation::CallData;
t("fromCalldata", fromCalldata); t("fromCalldata", fromCalldata);
bool fromMemory = fromStructType.location() == DataLocation::Memory;
t("fromMemory", fromMemory);
bool fromStorage = fromStructType.location() == DataLocation::Storage;
t("fromStorage", fromStorage);
t("isValueType", memberType.isValueType());
t("memberValues", suffixedVariableNameList("memberValue_", 0, memberType.stackItems().size()));
t("memberStorageSlotDiff", slotDiff.str());
if (fromCalldata) if (fromCalldata)
{ {
t("memberCalldataOffset", suffixedVariableNameList( t("memberOffset", to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)));
"memberCalldataOffset_", t("dynamicallyEncodedMember", memberType.isDynamicallyEncoded());
0, if (memberType.isDynamicallyEncoded())
structMembers[i].type->stackItems().size() t("accessCalldataTail", accessCalldataTailFunction(memberType));
)); if (memberType.isValueType())
t("dynamicallyEncodedMember", structMembers[i].type->isDynamicallyEncoded()); t("read", readFromCalldata(memberType));
if (structMembers[i].type->isDynamicallyEncoded())
t("accessCalldataTail", accessCalldataTailFunction(*structMembers[i].type));
} }
t("isValueType", structMembers[i].type->isValueType()); else if (fromMemory)
t("memberValues", suffixedVariableNameList( {
"memberValue_", t("memberOffset", fromStructType.memoryOffsetOfMember(structMembers[i].name).str());
0, t("read", readFromMemory(memberType));
structMembers[i].type->stackItems().size() }
else if (fromStorage)
{
auto [srcSlotOffset, srcOffset] = fromStructType.storageOffsetsOfMember(structMembers[i].name);
t("memberOffset", formatNumber(srcSlotOffset));
if (memberType.isValueType())
t("read", readFromStorageValueType(memberType, srcOffset, false));
else
solAssert(srcOffset == 0, "");
}
t("updateStorageValue", updateStorageValueFunction(
memberType,
*toStructMembers[i].type,
memberType.isValueType() ? optional<unsigned>{offset} : std::nullopt
)); ));
t("hasOffset", structMembers[i].type->isValueType());
t(
"updateMember",
structMembers[i].type->isValueType() ?
updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type) :
updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type, offset)
);
t("memberStorageSlotDiff", slotDiff.str());
t("memberStorageOffset", to_string(offset));
t(
"memberOffset",
fromCalldata ?
to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)) :
fromStructType.memoryOffsetOfMember(structMembers[i].name).str()
);
if (!fromCalldata || structMembers[i].type->isValueType())
t("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*structMembers[i].type, fromCalldata));
memberParams[i]["updateMemberCall"] = t.render(); memberParams[i]["updateMemberCall"] = t.render();
} }
templ("member", memberParams); templ("member", memberParams);

View File

@ -32,5 +32,7 @@ contract test {
} }
} }
// ====
// compileViaYul: also
// ---- // ----
// assign() -> 2, 2, 3, 3 // assign() -> 2, 2, 3, 3

View File

@ -20,6 +20,8 @@ contract c {
return data1.data[i]; return data1.data[i];
} }
} }
// ====
// compileViaYul: also
// ---- // ----
// storage: empty // storage: empty
// set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true // set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true

View File

@ -34,6 +34,8 @@ contract c {
} }
} }
// ====
// compileViaYul: also
// ---- // ----
// set(uint256): 7 -> true // set(uint256): 7 -> true
// retrieve(uint256): 7 -> 1, 3, 4, 2 // retrieve(uint256): 7 -> 1, 3, 4, 2

View File

@ -23,6 +23,8 @@ contract c {
} }
} }
// ====
// compileViaYul: also
// ---- // ----
// x() -> 0, 0 // x() -> 0, 0
// y() -> 0, 0 // y() -> 0, 0