mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Sol->Yul] Implementing struct copying from memory to storage
This commit is contained in:
parent
5e66583b0b
commit
b89c863e11
@ -1458,18 +1458,24 @@ string YulUtilFunctions::readFromCalldata(Type const& _type)
|
|||||||
return readFromMemoryOrCalldata(_type, true);
|
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 =
|
string const functionName =
|
||||||
"update_storage_value_" +
|
"update_storage_value_" +
|
||||||
(_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") +
|
(_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") +
|
||||||
_type.identifier();
|
(_fromType ? "_from_" + _fromType->identifier() : "") +
|
||||||
|
"_to_" +
|
||||||
|
_toType.identifier();
|
||||||
|
|
||||||
return m_functionCollector.createFunction(functionName, [&] {
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
if (_type.isValueType())
|
if (_toType.isValueType())
|
||||||
{
|
{
|
||||||
solAssert(_type.storageBytes() <= 32, "Invalid storage bytes size.");
|
solAssert(_toType.storageBytes() <= 32, "Invalid storage bytes size.");
|
||||||
solAssert(_type.storageBytes() > 0, "Invalid storage bytes size.");
|
solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size.");
|
||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(slot, <offset>value) {
|
function <functionName>(slot, <offset>value) {
|
||||||
@ -1480,19 +1486,68 @@ string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::opti
|
|||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("update",
|
("update",
|
||||||
_offset.has_value() ?
|
_offset.has_value() ?
|
||||||
updateByteSliceFunction(_type.storageBytes(), *_offset) :
|
updateByteSliceFunction(_toType.storageBytes(), *_offset) :
|
||||||
updateByteSliceFunctionDynamic(_type.storageBytes())
|
updateByteSliceFunctionDynamic(_toType.storageBytes())
|
||||||
)
|
)
|
||||||
("offset", _offset.has_value() ? "" : "offset, ")
|
("offset", _offset.has_value() ? "" : "offset, ")
|
||||||
("prepare", prepareStoreFunction(_type))
|
("prepare", prepareStoreFunction(_toType))
|
||||||
.render();
|
.render();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_type.category() == Type::Category::Array)
|
if (_toType.category() == Type::Category::Array)
|
||||||
solUnimplementedAssert(false, "");
|
|
||||||
else if (_type.category() == Type::Category::Struct)
|
|
||||||
solUnimplementedAssert(false, "");
|
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
|
else
|
||||||
solAssert(false, "Invalid non-value type for assignment.");
|
solAssert(false, "Invalid non-value type for assignment.");
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,11 @@ public:
|
|||||||
/// the specified slot and offset. If offset is not given, it is expected as
|
/// the specified slot and offset. If offset is not given, it is expected as
|
||||||
/// runtime parameter.
|
/// runtime parameter.
|
||||||
/// signature: (slot, [offset,] value)
|
/// 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
|
/// Returns the name of a function that will write the given value to
|
||||||
/// the specified address.
|
/// the specified address.
|
||||||
|
@ -314,9 +314,9 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
|||||||
|
|
||||||
writeToLValue(*m_currentLValue, value);
|
writeToLValue(*m_currentLValue, value);
|
||||||
|
|
||||||
m_currentLValue.reset();
|
if (m_currentLValue->type.category() != Type::Category::Struct && *_assignment.annotation().type != *TypeProvider::emptyTuple())
|
||||||
if (*_assignment.annotation().type != *TypeProvider::emptyTuple())
|
|
||||||
define(_assignment, value);
|
define(_assignment, value);
|
||||||
|
m_currentLValue.reset();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2486,7 +2486,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
|||||||
offset = std::get<unsigned>(_storage.offset);
|
offset = std::get<unsigned>(_storage.offset);
|
||||||
|
|
||||||
m_code <<
|
m_code <<
|
||||||
m_utils.updateStorageValueFunction(_lvalue.type, offset) <<
|
m_utils.updateStorageValueFunction(_lvalue.type, &_value.type(), offset) <<
|
||||||
"(" <<
|
"(" <<
|
||||||
_storage.slot <<
|
_storage.slot <<
|
||||||
(
|
(
|
||||||
|
@ -4,15 +4,16 @@ contract C {
|
|||||||
struct S {
|
struct S {
|
||||||
uint256 a;
|
uint256 a;
|
||||||
uint256 b;
|
uint256 b;
|
||||||
|
bytes2 c;
|
||||||
}
|
}
|
||||||
|
|
||||||
function f(S calldata s) external pure returns (uint256, uint256) {
|
function f(S calldata s) external pure returns (uint256, uint256, byte) {
|
||||||
S memory m = s;
|
S memory m = s;
|
||||||
return (m.a, m.b);
|
return (m.a, m.b, m.c[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====
|
// ====
|
||||||
// compileViaYul: also
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f((uint256,uint256)): 42, 23 -> 42, 23
|
// f((uint256,uint256, bytes2)): 42, 23, "ab" -> 42, 23, "b"
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint32 a;
|
||||||
|
uint128 b;
|
||||||
|
uint256 c;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct X {
|
||||||
|
uint256 a;
|
||||||
|
S s;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint[79] r;
|
||||||
|
X x;
|
||||||
|
|
||||||
|
function f() external returns (uint32, uint128, uint256) {
|
||||||
|
X memory m = X(12, S(42, 23, 34));
|
||||||
|
x = m;
|
||||||
|
return (x.s.a, x.s.b, x.s.c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 42, 23, 34
|
@ -10,5 +10,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// s() -> 1, true
|
// s() -> 1, true
|
||||||
|
Loading…
Reference in New Issue
Block a user