From 50373ac1b0194dd19c2cc25901095ad7b490cba1 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 24 Jun 2020 18:14:29 +0200 Subject: [PATCH] Yul: Implement memory struct allocation --- libsolidity/codegen/YulUtilFunctions.cpp | 24 ++++++++-- libsolidity/codegen/YulUtilFunctions.h | 7 ++- .../codegen/ir/IRGeneratorForStatements.cpp | 48 ++++++++++++++++--- .../semanticTests/structs/global.sol | 2 + .../memory_struct_named_constructor.sol | 18 +++++++ .../memory_structs_as_function_args.sol | 2 + .../structs/memory_structs_nested.sol | 2 + .../structs/multislot_struct_allocation.sol | 24 ++++++++++ .../structs/nested_struct_allocation.sol | 18 +++++++ .../structs/simple_struct_allocation.sol | 14 ++++++ .../semanticTests/structs/structs.sol | 2 + 11 files changed, 150 insertions(+), 11 deletions(-) create mode 100644 test/libsolidity/semanticTests/structs/memory_struct_named_constructor.sol create mode 100644 test/libsolidity/semanticTests/structs/multislot_struct_allocation.sol create mode 100644 test/libsolidity/semanticTests/structs/nested_struct_allocation.sol create mode 100644 test/libsolidity/semanticTests/structs/simple_struct_allocation.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index f7d9711a2..7678a0bef 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1646,13 +1646,30 @@ string YulUtilFunctions::allocateAndInitializeMemoryArrayFunction(ArrayType cons }); } -string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType const& _type) +string YulUtilFunctions::allocateMemoryStructFunction(StructType const& _type) { - string functionName = "allocate_and_initialize_memory_struct_" + _type.identifier(); + string functionName = "allocate_memory_struct_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { Whiskers templ(R"( function () -> memPtr { memPtr := () + } + )"); + templ("functionName", functionName); + templ("alloc", allocationFunction()); + templ("allocSize", _type.memoryDataSize().str()); + + return templ.render(); + }); +} + +string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType const& _type) +{ + string functionName = "allocate_and_zero_memory_struct_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + Whiskers templ(R"( + function () -> memPtr { + memPtr := () let offset := memPtr <#member> mstore(offset, ()) @@ -1661,10 +1678,9 @@ string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType co } )"); templ("functionName", functionName); - templ("alloc", allocationFunction()); + templ("allocStruct", allocateMemoryStructFunction(_type)); TypePointers const& members = _type.memoryMemberTypes(); - templ("allocSize", _type.memoryDataSize().str()); vector> memberParams(members.size()); for (size_t i = 0; i < members.size(); ++i) diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index c23a7d735..c1d98cfe4 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -286,8 +286,13 @@ public: /// signature: (length) -> memPtr std::string allocateAndInitializeMemoryArrayFunction(ArrayType const& _type); + /// @returns the name of a function that allocates a memory struct (no + /// initialization takes place). + /// signature: () -> memPtr + std::string allocateMemoryStructFunction(StructType const& _type); + /// @returns the name of a function that allocates and zeroes a memory struct. - /// signature: (members) -> memPtr + /// signature: () -> memPtr std::string allocateAndInitializeMemoryStructFunction(StructType const& _type); /// @returns the name of the function that converts a value of type @a _from diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 3ec89a233..c14d65121 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -600,22 +600,30 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { solUnimplementedAssert( - _functionCall.annotation().kind == FunctionCallKind::FunctionCall || - _functionCall.annotation().kind == FunctionCallKind::TypeConversion, + _functionCall.annotation().kind != FunctionCallKind::Unset, "This type of function call is not yet implemented" ); - Type const& funcType = type(_functionCall.expression()); - if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { - solAssert(funcType.category() == Type::Category::TypeType, "Expected category to be TypeType"); + solAssert( + _functionCall.expression().annotation().type->category() == Type::Category::TypeType, + "Expected category to be TypeType" + ); solAssert(_functionCall.arguments().size() == 1, "Expected one argument for type conversion"); define(_functionCall, *_functionCall.arguments().front()); return; } - FunctionTypePointer functionType = dynamic_cast(&funcType); + FunctionTypePointer functionType = nullptr; + if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + { + auto const& type = dynamic_cast(*_functionCall.expression().annotation().type); + auto const& structType = dynamic_cast(*type.actualType()); + functionType = structType.constructorType(); + } + else + functionType = dynamic_cast(_functionCall.expression().annotation().type); TypePointers parameterTypes = functionType->parameterTypes(); vector> const& callArguments = _functionCall.arguments(); @@ -639,6 +647,34 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) arguments.push_back(callArguments[static_cast(std::distance(callArgumentNames.begin(), it))]); } + if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + { + TypeType const& type = dynamic_cast(*_functionCall.expression().annotation().type); + auto const& structType = dynamic_cast(*type.actualType()); + + define(_functionCall) << m_utils.allocateMemoryStructFunction(structType) << "()\n"; + + MemberList::MemberMap members = structType.nativeMembers(nullptr); + + solAssert(members.size() == arguments.size(), "Struct parameter mismatch."); + + for (size_t i = 0; i < arguments.size(); i++) + { + IRVariable converted = convert(*arguments[i], *parameterTypes[i]); + m_code << + m_utils.writeToMemoryFunction(*functionType->parameterTypes()[i]) << + "(add(" << + IRVariable(_functionCall).part("mpos").name() << + ", " << + structType.memoryOffsetOfMember(members[i].name) << + "), " << + converted.commaSeparatedList() << + ")\n"; + } + + return; + } + auto memberAccess = dynamic_cast(&_functionCall.expression()); if (memberAccess) { diff --git a/test/libsolidity/semanticTests/structs/global.sol b/test/libsolidity/semanticTests/structs/global.sol index 18dbf87dd..de8fe21b3 100644 --- a/test/libsolidity/semanticTests/structs/global.sol +++ b/test/libsolidity/semanticTests/structs/global.sol @@ -6,5 +6,7 @@ contract C { return (s.a, s.b); } } +// ==== +// compileViaYul: also // ---- // f((uint256,uint256)): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/structs/memory_struct_named_constructor.sol b/test/libsolidity/semanticTests/structs/memory_struct_named_constructor.sol new file mode 100644 index 000000000..6cf5378b4 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_struct_named_constructor.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { + uint256 a; + bool x; + } + + function s() public returns(S memory) + { + return S({x: true, a: 8}); + } +} + +// ==== +// compileViaYul: also +// ---- +// s() -> 8, true diff --git a/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol b/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol index 1173a3796..49bb6ba7a 100644 --- a/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol +++ b/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol @@ -28,5 +28,7 @@ contract Test { } } +// ==== +// compileViaYul: also // ---- // test() -> 1, 2, 3 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_nested.sol b/test/libsolidity/semanticTests/structs/memory_structs_nested.sol index f5c41a232..40dba7f26 100644 --- a/test/libsolidity/semanticTests/structs/memory_structs_nested.sol +++ b/test/libsolidity/semanticTests/structs/memory_structs_nested.sol @@ -38,5 +38,7 @@ contract Test { } } +// ==== +// compileViaYul: also // ---- // test() -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/structs/multislot_struct_allocation.sol b/test/libsolidity/semanticTests/structs/multislot_struct_allocation.sol new file mode 100644 index 000000000..9766880bf --- /dev/null +++ b/test/libsolidity/semanticTests/structs/multislot_struct_allocation.sol @@ -0,0 +1,24 @@ +contract C { + struct I { + uint b; + uint c; + function(uint) external returns (uint) x; + } + struct S { + I a; + } + + function o(uint a) external returns(uint) { return a+1; } + + function f() external returns (uint) { + S memory s = S(I(1,2, this.o)); + return s.a.x(1); + } +} + + + +// ==== +// compileViaYul: also +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/structs/nested_struct_allocation.sol b/test/libsolidity/semanticTests/structs/nested_struct_allocation.sol new file mode 100644 index 000000000..6a7954726 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/nested_struct_allocation.sol @@ -0,0 +1,18 @@ +contract C { + struct I { + uint b; + uint c; + } + struct S { + I a; + } + + function f() external returns (uint) { + S memory s = S(I(1,2)); + return s.a.b; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1 diff --git a/test/libsolidity/semanticTests/structs/simple_struct_allocation.sol b/test/libsolidity/semanticTests/structs/simple_struct_allocation.sol new file mode 100644 index 000000000..7e3a54073 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/simple_struct_allocation.sol @@ -0,0 +1,14 @@ +contract C { + struct S { + uint a; + } + + function f() external returns (uint) { + S memory s = S(1); + return s.a; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1 diff --git a/test/libsolidity/semanticTests/structs/structs.sol b/test/libsolidity/semanticTests/structs/structs.sol index 4491f625d..d0f63d892 100644 --- a/test/libsolidity/semanticTests/structs/structs.sol +++ b/test/libsolidity/semanticTests/structs/structs.sol @@ -27,6 +27,8 @@ contract test { data.recursive[4].z = 9; } } +// ==== +// compileViaYul: also // ---- // check() -> false // set() ->