[Sol->Yul] Implementing struct copying from memory to storage

This commit is contained in:
Djordje Mijovic
2020-09-02 12:28:53 +02:00
parent 5e66583b0b
commit b89c863e11
6 changed files with 108 additions and 18 deletions
+66 -11
View File
@@ -1458,18 +1458,24 @@ string YulUtilFunctions::readFromCalldata(Type const& _type)
return readFromMemoryOrCalldata(_type, true);
}
string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::optional<unsigned> const& _offset)
string YulUtilFunctions::updateStorageValueFunction(
Type const& _toType,
Type const* _fromType,
std::optional<unsigned> const& _offset
)
{
string const functionName =
"update_storage_value_" +
(_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") +
_type.identifier();
(_fromType ? "_from_" + _fromType->identifier() : "") +
"_to_" +
_toType.identifier();
return m_functionCollector.createFunction(functionName, [&] {
if (_type.isValueType())
if (_toType.isValueType())
{
solAssert(_type.storageBytes() <= 32, "Invalid storage bytes size.");
solAssert(_type.storageBytes() > 0, "Invalid storage bytes size.");
solAssert(_toType.storageBytes() <= 32, "Invalid storage bytes size.");
solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size.");
return Whiskers(R"(
function <functionName>(slot, <offset>value) {
@@ -1480,19 +1486,68 @@ string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::opti
("functionName", functionName)
("update",
_offset.has_value() ?
updateByteSliceFunction(_type.storageBytes(), *_offset) :
updateByteSliceFunctionDynamic(_type.storageBytes())
updateByteSliceFunction(_toType.storageBytes(), *_offset) :
updateByteSliceFunctionDynamic(_toType.storageBytes())
)
("offset", _offset.has_value() ? "" : "offset, ")
("prepare", prepareStoreFunction(_type))
("prepare", prepareStoreFunction(_toType))
.render();
}
else
{
if (_type.category() == Type::Category::Array)
solUnimplementedAssert(false, "");
else if (_type.category() == Type::Category::Struct)
if (_toType.category() == Type::Category::Array)
solUnimplementedAssert(false, "");
else if (_toType.category() == Type::Category::Struct)
{
solAssert(_fromType, "");
solAssert(_fromType->category() == Type::Category::Struct, "");
auto const& fromStructType = dynamic_cast<StructType const&>(*_fromType);
auto const& toStructType = dynamic_cast<StructType const&>(_toType);
solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
solUnimplementedAssert(fromStructType.location() == DataLocation::Memory, "");
solUnimplementedAssert(_offset.has_value() && _offset.value() == 0, "");
Whiskers templ(R"(
function <functionName>(slot, value) {
<#member>
{
<updateMemberCall>
}
</member>
}
)");
templ("functionName", functionName);
MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr);
MemberList::MemberMap fromStructMembers = fromStructType.nativeMembers(nullptr);
vector<map<string, string>> memberParams(toStructMembers.size());
for (size_t i = 0; i < toStructMembers.size(); ++i)
{
solAssert(toStructMembers[i].type->memoryHeadSize() == 32, "");
bool isStruct = toStructMembers[i].type->category() == Type::Category::Struct;
auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(toStructMembers[i].name);
memberParams[i]["updateMemberCall"] = Whiskers(R"(
let memberValue := <loadFromMemory>(add(valueMem, <memberMemoryOffset>))
<updateMember>(add(slot, <memberStorageSlotDiff>), <?hasOffset><memberStorageOffset>,</hasOffset> memberValue)
)")
("hasOffset", !isStruct)
(
"updateMember",
isStruct ?
updateStorageValueFunction(*toStructMembers[i].type, fromStructMembers[i].type, offset) :
updateStorageValueFunction(*toStructMembers[i].type, fromStructMembers[i].type)
)
("memberStorageSlotDiff", slotDiff.str())
("memberStorageOffset", to_string(offset))
("memberMemoryOffset", fromStructType.memoryOffsetOfMember(fromStructMembers[i].name).str())
("loadFromMemory", readFromMemory(*fromStructMembers[i].type))
.render();
}
templ("member", memberParams);
return templ.render();
}
else
solAssert(false, "Invalid non-value type for assignment.");
}
+5 -1
View File
@@ -248,7 +248,11 @@ public:
/// the specified slot and offset. If offset is not given, it is expected as
/// runtime parameter.
/// signature: (slot, [offset,] value)
std::string updateStorageValueFunction(Type const& _type, std::optional<unsigned> const& _offset = std::optional<unsigned>());
std::string updateStorageValueFunction(
Type const& _toType,
Type const* _fromType = nullptr,
std::optional<unsigned> const& _offset = std::optional<unsigned>()
);
/// Returns the name of a function that will write the given value to
/// the specified address.
@@ -314,9 +314,9 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
writeToLValue(*m_currentLValue, value);
m_currentLValue.reset();
if (*_assignment.annotation().type != *TypeProvider::emptyTuple())
if (m_currentLValue->type.category() != Type::Category::Struct && *_assignment.annotation().type != *TypeProvider::emptyTuple())
define(_assignment, value);
m_currentLValue.reset();
return false;
}
@@ -2486,7 +2486,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
offset = std::get<unsigned>(_storage.offset);
m_code <<
m_utils.updateStorageValueFunction(_lvalue.type, offset) <<
m_utils.updateStorageValueFunction(_lvalue.type, &_value.type(), offset) <<
"(" <<
_storage.slot <<
(