From 80ce3ca66f063d8d87c2393e689f92d8608b4e0a Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 18:58:12 +0200 Subject: [PATCH] Move ABI encoder into its own function. --- Changelog.md | 1 + libsolidity/codegen/ABIFunctions.cpp | 104 ++++++++++++----------- libsolidity/codegen/ABIFunctions.h | 15 ++-- libsolidity/codegen/CompilerContext.h | 5 ++ libsolidity/codegen/CompilerUtils.cpp | 17 ++-- libsolidity/codegen/ContractCompiler.cpp | 5 ++ 6 files changed, 82 insertions(+), 65 deletions(-) diff --git a/Changelog.md b/Changelog.md index e46a7c2f4..0b9295d06 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * Support ``pragma experimental v0.5.0;`` to turn on upcoming breaking changes. * Code Generator: Added ``.selector`` member on external function types to retrieve their signature. + * Code Generator: Keep a single copy of encoding functions when using the experimental "ABIEncoderV2". * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. * Syntax Checker: Warn if no visibility is specified on contract functions. * Type Checker: Display helpful warning for unused function arguments/return parameters. diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index a2938ed79..3a9f1a486 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -30,65 +30,73 @@ using namespace std; using namespace dev; using namespace dev::solidity; -ABIFunctions::~ABIFunctions() -{ - // This throws an exception and thus might cause immediate termination, but hey, - // it's a failed assertion anyway :-) - solAssert(m_requestedFunctions.empty(), "Forgot to call ``requestedFunctions()``."); -} - string ABIFunctions::tupleEncoder( TypePointers const& _givenTypes, TypePointers const& _targetTypes, bool _encodeAsLibraryTypes ) { - // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart> + string functionName = string("abi_encode_tuple_"); + for (auto const& t: _givenTypes) + functionName += t->identifier() + "_"; + functionName += "_to_"; + for (auto const& t: _targetTypes) + functionName += t->identifier() + "_"; + if (_encodeAsLibraryTypes) + functionName += "_library"; - solAssert(!_givenTypes.empty(), ""); - size_t const headSize_ = headSize(_targetTypes); + return createFunction(functionName, [&]() { + solAssert(!_givenTypes.empty(), ""); - Whiskers encoder(R"( + // Note that the values are in reverse due to the difference in calling semantics. + Whiskers templ(R"( + function (headStart ) -> tail { + tail := add(headStart, ) + + } + )"); + templ("functionName", functionName); + size_t const headSize_ = headSize(_targetTypes); + templ("headSize", to_string(headSize_)); + string valueParams; + string encodeElements; + size_t headPos = 0; + size_t stackPos = 0; + for (size_t i = 0; i < _givenTypes.size(); ++i) { - let tail := add($headStart, ) - - := tail + 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"( + mstore(add(headStart, ), sub(tail, headStart)) + tail := ( tail) + )") : + string(R"( + ( add(headStart, )) + )") + ); + elementTempl("values", valueNames); + elementTempl("pos", to_string(headPos)); + elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, false)); + encodeElements += elementTempl.render(); + headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); } - )"); - encoder("headSize", to_string(headSize_)); - string encodeElements; - size_t headPos = 0; - 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++) + ", "; - bool dynamic = _targetTypes[i]->isDynamicallyEncoded(); - Whiskers elementTempl( - dynamic ? - string(R"( - mstore(add($headStart, ), sub(tail, $headStart)) - tail := ( tail) - )") : - string(R"( - ( add($headStart, )) - )") - ); - elementTempl("values", valueNames); - elementTempl("pos", to_string(headPos)); - elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, false)); - encodeElements += elementTempl.render(); - headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); - } - solAssert(headPos == headSize_, ""); - encoder("encodeElements", encodeElements); - encoder("deepestStackElement", stackPos > 0 ? "$value0" : "$headStart"); + solAssert(headPos == headSize_, ""); + templ("valueParams", valueParams); + templ("encodeElements", encodeElements); - return encoder.render(); + return templ.render(); + }); } string ABIFunctions::requestedFunctions() diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index e43e2323a..5bbd842f8 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -44,15 +44,18 @@ using TypePointers = std::vector; /// multiple times. /// /// Make sure to include the result of ``requestedFunctions()`` to a block that -/// is visible from the code that was generated here. +/// is visible from the code that was generated here, or use named labels. class ABIFunctions { public: - ~ABIFunctions(); - - /// @returns assembly code block to ABI-encode values of @a _givenTypes residing on the stack + /// @returns name of an assembly function to ABI-encode values of @a _givenTypes /// into memory, converting the types to @a _targetTypes on the fly. - /// Assumed variables to be present: <$value0> <$value1> ... <$value(n-1)> <$headStart> + /// 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 memory head 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) @@ -63,7 +66,7 @@ public: bool _encodeAsLibraryTypes = false ); - /// @returns auxiliary functions referenced from the block generated in @a tupleEncoder + /// @returns concatenation of all generated functions. std::string requestedFunctions(); private: diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index dd36bba08..7743fd3f0 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -22,6 +22,8 @@ #pragma once +#include + #include #include #include @@ -121,6 +123,7 @@ public: ); /// Generates the code for missing low-level functions, i.e. calls the generators passed above. void appendMissingLowLevelFunctions(); + ABIFunctions& abiFunctions() { return m_abiFunctions; } ModifierDefinition const& functionModifier(std::string const& _name) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). @@ -302,6 +305,8 @@ private: size_t m_runtimeSub = -1; /// An index of low-level function labels by name. std::map m_lowLevelFunctions; + /// Container for ABI functions to be generated. + ABIFunctions m_abiFunctions; /// The queue of low-level functions to generate. std::queue>> m_lowLevelFunctionGenerationQueue; }; diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 3662478db..1e623357a 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -310,18 +310,13 @@ void CompilerUtils::abiEncode( { // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart> - vector variables; - size_t numValues = sizeOnStack(_givenTypes); - for (size_t i = 0; i < numValues; ++i) - variables.push_back("$value" + to_string(i)); - variables.push_back("$headStart"); + auto ret = m_context.pushNewTag(); + moveIntoStack(sizeOnStack(_givenTypes) + 1); - ABIFunctions funs; - string routine = funs.tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); - routine += funs.requestedFunctions(); - m_context.appendInlineAssembly("{" + routine + "}", variables, true); - // Remove everyhing except for "value0" / the final memory pointer. - popStackSlots(numValues); + string encoderName = m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); + m_context.appendJumpTo(m_context.namedTag(encoderName)); + m_context.adjustStackOffset(-int(sizeOnStack(_givenTypes)) - 1); + m_context << ret.tag(); } void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 24d3d959f..514963681 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -122,6 +122,7 @@ void ContractCompiler::appendCallValueCheck() void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) { + CompilerContext::LocationSetter locationSetter(m_context, _contract); // Determine the arguments that are used for the base constructors. std::vector const& bases = _contract.annotation().linearizedBaseContracts; for (ContractDefinition const* contract: bases) @@ -174,6 +175,7 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont appendMissingFunctions(); m_runtimeCompiler->appendMissingFunctions(); + CompilerContext::LocationSetter locationSetter(m_context, _contract); m_context << deployRoutine; solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); @@ -892,6 +894,9 @@ void ContractCompiler::appendMissingFunctions() solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); } m_context.appendMissingLowLevelFunctions(); + string abiFunctions = m_context.abiFunctions().requestedFunctions(); + if (!abiFunctions.empty()) + m_context.appendInlineAssembly("{" + move(abiFunctions) + "}", {}, true); } void ContractCompiler::appendModifierOrFunctionCode()