aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp104
-rw-r--r--libsolidity/codegen/ABIFunctions.h15
-rw-r--r--libsolidity/codegen/CompilerContext.cpp16
-rw-r--r--libsolidity/codegen/CompilerContext.h10
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp19
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp5
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp16
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.h3
8 files changed, 105 insertions, 83 deletions
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index a2938ed7..3a9f1a48 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 <functionName>(headStart <valueParams>) -> tail {
+ tail := add(headStart, <headSize>)
+ <encodeElements>
+ }
+ )");
+ 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, <headSize>)
- <encodeElements>
- <deepestStackElement> := 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, <pos>), sub(tail, headStart))
+ tail := <abiEncode>(<values> tail)
+ )") :
+ string(R"(
+ <abiEncode>(<values> add(headStart, <pos>))
+ )")
+ );
+ 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, <pos>), sub(tail, $headStart))
- tail := <abiEncode>(<values> tail)
- )") :
- string(R"(
- <abiEncode>(<values> add($headStart, <pos>))
- )")
- );
- 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 e43e2323..5bbd842f 100644
--- a/libsolidity/codegen/ABIFunctions.h
+++ b/libsolidity/codegen/ABIFunctions.h
@@ -44,15 +44,18 @@ using TypePointers = std::vector<TypePointer>;
/// 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: <headStart> <value_n> ... <value_1>, i.e.
+ /// the layout on the stack is <value_1> ... <value_n> <headStart> 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.cpp b/libsolidity/codegen/CompilerContext.cpp
index ed780d0b..5a77162e 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -266,19 +266,9 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
void CompilerContext::appendInlineAssembly(
string const& _assembly,
vector<string> const& _localVariables,
- map<string, string> const& _replacements
+ bool _system
)
{
- string replacedAssembly;
- string const* assembly = &_assembly;
- if (!_replacements.empty())
- {
- replacedAssembly = _assembly;
- for (auto const& replacement: _replacements)
- replacedAssembly = boost::algorithm::replace_all_copy(replacedAssembly, replacement.first, replacement.second);
- assembly = &replacedAssembly;
- }
-
int startStackHeight = stackHeight();
julia::ExternalIdentifierAccess identifierAccess;
@@ -320,7 +310,7 @@ void CompilerContext::appendInlineAssembly(
ErrorList errors;
ErrorReporter errorReporter(errors);
- auto scanner = make_shared<Scanner>(CharStream(*assembly), "--CODEGEN--");
+ auto scanner = make_shared<Scanner>(CharStream(_assembly), "--CODEGEN--");
auto parserResult = assembly::Parser(errorReporter).parse(scanner);
solAssert(parserResult, "Failed to parse inline assembly block.");
solAssert(errorReporter.errors().empty(), "Failed to parse inline assembly block.");
@@ -329,7 +319,7 @@ void CompilerContext::appendInlineAssembly(
assembly::AsmAnalyzer analyzer(analysisInfo, errorReporter, false, identifierAccess.resolve);
solAssert(analyzer.analyze(*parserResult), "Failed to analyze inline assembly block.");
solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block.");
- assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess);
+ assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system);
}
FunctionDefinition const& CompilerContext::resolveVirtualFunction(
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 5116585e..7743fd3f 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -22,6 +22,8 @@
#pragma once
+#include <libsolidity/codegen/ABIFunctions.h>
+
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/ASTAnnotations.h>
@@ -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).
@@ -156,6 +159,8 @@ public:
eth::AssemblyItem pushNewTag() { return m_asm->append(m_asm->newPushTag()).tag(); }
/// @returns a new tag without pushing any opcodes or data
eth::AssemblyItem newTag() { return m_asm->newTag(); }
+ /// @returns a new tag identified by name.
+ eth::AssemblyItem namedTag(std::string const& _name) { return m_asm->namedTag(_name); }
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the pushsub assembly item.
eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); }
@@ -185,10 +190,11 @@ public:
/// Appends inline assembly. @a _replacements are string-matching replacements that are performed
/// prior to parsing the inline assembly.
/// @param _localVariables assigns stack positions to variables with the last one being the stack top
+ /// @param _system if true, this is a "system-level" assembly where all functions use named labels.
void appendInlineAssembly(
std::string const& _assembly,
std::vector<std::string> const& _localVariables = std::vector<std::string>(),
- std::map<std::string, std::string> const& _replacements = std::map<std::string, std::string>{}
+ bool _system = false
);
/// Appends arbitrary data to the end of the bytecode.
@@ -299,6 +305,8 @@ private:
size_t m_runtimeSub = -1;
/// An index of low-level function labels by name.
std::map<std::string, eth::AssemblyItem> m_lowLevelFunctions;
+ /// Container for ABI functions to be generated.
+ ABIFunctions m_abiFunctions;
/// The queue of low-level functions to generate.
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
};
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 146472f9..1e623357 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<string> 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");
-
- ABIFunctions funs;
- string routine = funs.tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes);
- routine += funs.requestedFunctions();
- m_context.appendInlineAssembly("{" + routine + "}", variables);
- // Remove everyhing except for "value0" / the final memory pointer.
- popStackSlots(numValues);
+ auto ret = m_context.pushNewTag();
+ moveIntoStack(sizeOnStack(_givenTypes) + 1);
+
+ 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 24d3d959..51496368 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<ContractDefinition const*> 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()
diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp
index 6d0c0255..dded9f76 100644
--- a/libsolidity/inlineasm/AsmCodeGen.cpp
+++ b/libsolidity/inlineasm/AsmCodeGen.cpp
@@ -83,6 +83,10 @@ public:
{
return assemblyTagToIdentifier(m_assembly.newTag());
}
+ virtual size_t namedLabel(std::string const& _name) override
+ {
+ return assemblyTagToIdentifier(m_assembly.namedTag(_name));
+ }
virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override
{
m_assembly.appendLibraryAddress(_linkerSymbol);
@@ -141,9 +145,17 @@ void assembly::CodeGenerator::assemble(
Block const& _parsedData,
AsmAnalysisInfo& _analysisInfo,
eth::Assembly& _assembly,
- julia::ExternalIdentifierAccess const& _identifierAccess
+ julia::ExternalIdentifierAccess const& _identifierAccess,
+ bool _useNamedLabelsForFunctions
)
{
EthAssemblyAdapter assemblyAdapter(_assembly);
- julia::CodeTransform(assemblyAdapter, _analysisInfo, false, false, _identifierAccess)(_parsedData);
+ julia::CodeTransform(
+ assemblyAdapter,
+ _analysisInfo,
+ false,
+ false,
+ _identifierAccess,
+ _useNamedLabelsForFunctions
+ )(_parsedData);
}
diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h
index 2a36a590..a7d7ead1 100644
--- a/libsolidity/inlineasm/AsmCodeGen.h
+++ b/libsolidity/inlineasm/AsmCodeGen.h
@@ -46,7 +46,8 @@ public:
Block const& _parsedData,
AsmAnalysisInfo& _analysisInfo,
eth::Assembly& _assembly,
- julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess()
+ julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess(),
+ bool _useNamedLabelsForFunctions = false
);
};