diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index a672457cb..1d532f5da 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -107,6 +107,74 @@ string ABIFunctions::tupleEncoder( }); } +string ABIFunctions::tupleEncoderPacked( + TypePointers const& _givenTypes, + TypePointers const& _targetTypes +) +{ + EncodingOptions options; + options.encodeAsLibraryTypes = false; + options.encodeFunctionFromStack = true; + options.padded = false; + options.dynamicInplace = true; + + string functionName = string("abi_encode_tuple_packed_"); + for (auto const& t: _givenTypes) + functionName += t->identifier() + "_"; + functionName += "_to_"; + for (auto const& t: _targetTypes) + functionName += t->identifier() + "_"; + functionName += options.toFunctionNameSuffix(); + + return createExternallyUsedFunction(functionName, [&]() { + solAssert(!_givenTypes.empty(), ""); + + // Note that the values are in reverse due to the difference in calling semantics. + Whiskers templ(R"( + function (pos ) -> end { + + end := pos + } + )"); + templ("functionName", functionName); + string valueParams; + string encodeElements; + size_t stackPos = 0; + for (size_t i = 0; i < _givenTypes.size(); ++i) + { + solAssert(_givenTypes[i], ""); + solAssert(_targetTypes[i], ""); + size_t sizeOnStack = _givenTypes[i]->sizeOnStack(); + string valueNames = ""; + for (size_t j = 0; j < sizeOnStack; j++) + { + valueNames += "value" + to_string(stackPos) + ", "; + valueParams = ", value" + to_string(stackPos) + valueParams; + stackPos++; + } + bool dynamic = _targetTypes[i]->isDynamicallyEncoded(); + Whiskers elementTempl( + dynamic ? + string(R"( + pos := ( pos) + )") : + string(R"( + ( pos) + pos := add(pos, ) + )") + ); + elementTempl("values", valueNames); + if (!dynamic) + elementTempl("calldataEncodedSize", to_string(_targetTypes[i]->calldataEncodedSize(false))); + elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], options)); + encodeElements += elementTempl.render(); + } + templ("valueParams", valueParams); + templ("encodeElements", encodeElements); + + return templ.render(); + }); +} string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) { string functionName = string("abi_decode_tuple_"); diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 9aaddba4d..a8a3c64e5 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -60,16 +60,26 @@ public: /// The values represent stack slots. If a type occupies more or less than one /// stack slot, it takes exactly that number of values. /// Returns a pointer to the end of the area written in memory. - /// Does not allocate memory (does not change the memory head pointer), but writes + /// Does not allocate memory (does not change the free memory pointer), but writes /// to memory starting at $headStart and an unrestricted amount after that. - /// Assigns the end of encoded memory either to $value0 or (if that is not present) - /// to $headStart. std::string tupleEncoder( TypePointers const& _givenTypes, TypePointers const& _targetTypes, bool _encodeAsLibraryTypes = false ); + /// @returns name of an assembly function to encode values of @a _givenTypes + /// with packed encoding into memory, converting the types to @a _targetTypes on the fly. + /// Parameters are: ... , i.e. + /// the layout on the stack is ... with + /// the top of the stack on the right. + /// The values represent stack slots. If a type occupies more or less than one + /// stack slot, it takes exactly that number of values. + /// Returns a pointer to the end of the area written in memory. + /// Does not allocate memory (does not change the free memory pointer), but writes + /// to memory starting at memPos and an unrestricted amount after that. + std::string tupleEncoderPacked(TypePointers const& _givenTypes, TypePointers const& _targetTypes); + /// @returns name of an assembly function to ABI-decode values of @a _types /// into memory. If @a _fromMemory is true, decodes from memory instead of /// from calldata. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 6cfb07778..676dd5b60 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -348,11 +348,15 @@ void CompilerUtils::encodeToMemory( if (_givenTypes.empty()) return; - else if (_padToWordBoundaries && !_copyDynamicDataInPlace && encoderV2) + if (encoderV2) { // Use the new Yul-based encoding function + solAssert( + _padToWordBoundaries != _copyDynamicDataInPlace, + "Non-padded and in-place encoding can only be combined." + ); auto stackHeightBefore = m_context.stackHeight(); - abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes); + abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes, _padToWordBoundaries); solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes), ""); return; } @@ -466,15 +470,22 @@ void CompilerUtils::encodeToMemory( void CompilerUtils::abiEncodeV2( TypePointers const& _givenTypes, TypePointers const& _targetTypes, - bool _encodeAsLibraryTypes + bool _encodeAsLibraryTypes, + bool _padToWordBoundaries ) { + if (!_padToWordBoundaries) + solAssert(!_encodeAsLibraryTypes, "Library calls cannot be packed."); + // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart> auto ret = m_context.pushNewTag(); moveIntoStack(sizeOnStack(_givenTypes) + 1); - string encoderName = m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); + string encoderName = + _padToWordBoundaries ? + m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) : + m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes); m_context.appendJumpTo(m_context.namedTag(encoderName)); m_context.adjustStackOffset(-int(sizeOnStack(_givenTypes)) - 1); m_context << ret.tag(); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 6bde2e8bd..d095e05f9 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -159,7 +159,8 @@ public: void abiEncodeV2( TypePointers const& _givenTypes, TypePointers const& _targetTypes, - bool _encodeAsLibraryTypes = false + bool _encodeAsLibraryTypes = false, + bool _padToWordBoundaries = true ); /// Decodes data from ABI encoding into internal encoding. If @a _fromMemory is set to true,