aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r--libsolidity/codegen/Compiler.cpp11
-rw-r--r--libsolidity/codegen/Compiler.h8
-rw-r--r--libsolidity/codegen/CompilerContext.cpp16
-rw-r--r--libsolidity/codegen/CompilerContext.h45
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp13
-rw-r--r--libsolidity/codegen/CompilerUtils.h4
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp43
-rw-r--r--libsolidity/codegen/ContractCompiler.h10
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp31
9 files changed, 122 insertions, 59 deletions
diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp
index bb8211ad..eefa50c5 100644
--- a/libsolidity/codegen/Compiler.cpp
+++ b/libsolidity/codegen/Compiler.cpp
@@ -33,11 +33,13 @@ void Compiler::compileContract(
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
)
{
- ContractCompiler runtimeCompiler(CompilationMode::Runtime, nullptr, m_runtimeContext, m_optimize);
+ ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
runtimeCompiler.compileContract(_contract, _contracts);
- ContractCompiler creationCompiler(CompilationMode::Creation, &m_runtimeContext, m_context, m_optimize);
- m_runtimeSub = creationCompiler.compileConstructor(m_runtimeContext, _contract, _contracts);
+ // This might modify m_runtimeContext because it can access runtime functions at
+ // creation time.
+ ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize);
+ m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
if (m_optimize)
m_context.optimise(m_optimizeRuns);
@@ -54,7 +56,8 @@ void Compiler::compileClone(
map<ContractDefinition const*, eth::Assembly const*> const& _contracts
)
{
- ContractCompiler cloneCompiler(CompilationMode::Creation, &m_runtimeContext, m_context, m_optimize);
+ ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
+ ContractCompiler cloneCompiler(&runtimeCompiler, m_context, m_optimize);
m_runtimeSub = cloneCompiler.compileClone(_contract, _contracts);
if (m_optimize)
diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h
index 56849ea0..4a87de0e 100644
--- a/libsolidity/codegen/Compiler.h
+++ b/libsolidity/codegen/Compiler.h
@@ -36,8 +36,8 @@ public:
explicit Compiler(bool _optimize = false, unsigned _runs = 200):
m_optimize(_optimize),
m_optimizeRuns(_runs),
- m_context(CompilationMode::Creation, &m_runtimeContext),
- m_runtimeContext(CompilationMode::Runtime)
+ m_runtimeContext(),
+ m_context(&m_runtimeContext)
{ }
void compileContract(
@@ -71,9 +71,9 @@ public:
private:
bool const m_optimize;
unsigned const m_optimizeRuns;
- CompilerContext m_context;
- size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present.
CompilerContext m_runtimeContext;
+ size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present.
+ CompilerContext m_context;
};
}
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 3ac5bd3c..f1d306ce 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -92,22 +92,22 @@ eth::AssemblyItem CompilerContext::functionEntryLabelIfExists(Declaration const&
return m_functionCompilationQueue.entryLabelIfExists(_declaration);
}
-eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(FunctionDefinition const& _function)
+FunctionDefinition const& CompilerContext::resolveVirtualFunction(FunctionDefinition const& _function)
{
// Libraries do not allow inheritance and their functions can be inlined, so we should not
// search the inheritance hierarchy (which will be the wrong one in case the function
// is inlined).
if (auto scope = dynamic_cast<ContractDefinition const*>(_function.scope()))
if (scope->isLibrary())
- return functionEntryLabel(_function);
+ return _function;
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
- return virtualFunctionEntryLabel(_function, m_inheritanceHierarchy.begin());
+ return resolveVirtualFunction(_function, m_inheritanceHierarchy.begin());
}
-eth::AssemblyItem CompilerContext::superFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base)
+FunctionDefinition const& CompilerContext::superFunction(FunctionDefinition const& _function, ContractDefinition const& _base)
{
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
- return virtualFunctionEntryLabel(_function, superContract(_base));
+ return resolveVirtualFunction(_function, superContract(_base));
}
FunctionDefinition const* CompilerContext::nextConstructor(ContractDefinition const& _contract) const
@@ -227,7 +227,7 @@ void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
sub.injectStart(fromBigEndian<u256>(binaryVersion()));
}
-eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(
+FunctionDefinition const& CompilerContext::resolveVirtualFunction(
FunctionDefinition const& _function,
vector<ContractDefinition const*>::const_iterator _searchStart
)
@@ -242,9 +242,9 @@ eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(
!function->isConstructor() &&
FunctionType(*function).hasEqualArgumentTypes(functionType)
)
- return functionEntryLabel(*function);
+ return *function;
solAssert(false, "Super function " + name + " not found.");
- return m_asm.newTag(); // not reached
+ return _function; // not reached
}
vector<ContractDefinition const*>::const_iterator CompilerContext::superContract(ContractDefinition const& _contract) const
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 8b95c9f5..c4724ee0 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -37,10 +37,6 @@ namespace dev {
namespace solidity {
-/// Depending on the compilation is on the runtime code or the creation code,
-/// the interpretation of internal function values differ.
-enum class CompilationMode { Runtime, Creation };
-
/**
* Context to be shared by all units that compile the same contract.
* It stores the generated bytecode and the position of identifiers in memory and on the stack.
@@ -48,15 +44,13 @@ enum class CompilationMode { Runtime, Creation };
class CompilerContext
{
public:
- CompilerContext(CompilationMode _mode, CompilerContext* _runtimeContext = nullptr) :
- m_mode(_mode), m_runtimeContext(_runtimeContext)
+ CompilerContext(CompilerContext* _runtimeContext = nullptr) :
+ m_runtimeContext(_runtimeContext)
{
- solAssert(m_mode != CompilationMode::Runtime || !m_runtimeContext, "runtime but another runtime context provided");
- solAssert(m_mode != CompilationMode::Creation || m_runtimeContext, "creation but no runtime context provided");
+ if (m_runtimeContext)
+ m_runtimeSub = registerSubroutine(m_runtimeContext->assembly());
}
- bool isCreationPhase() const { return m_mode == CompilationMode::Creation; }
-
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
@@ -80,10 +74,10 @@ public:
eth::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const;
void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
/// @returns the entry label of the given function and takes overrides into account.
- eth::AssemblyItem virtualFunctionEntryLabel(FunctionDefinition const& _function);
- /// @returns the entry label of a function that overrides the given declaration from the most derived class just
+ FunctionDefinition const& resolveVirtualFunction(FunctionDefinition const& _function);
+ /// @returns the function that overrides the given declaration from the most derived class just
/// above _base in the current inheritance hierarchy.
- eth::AssemblyItem superFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base);
+ FunctionDefinition const& superFunction(FunctionDefinition const& _function, ContractDefinition const& _base);
FunctionDefinition const* nextConstructor(ContractDefinition const& _contract) const;
/// @returns the next function in the queue of functions that are still to be compiled
@@ -123,11 +117,15 @@ 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(); }
+ /// Adds a subroutine to the code (in the data section)
+ /// @returns the assembly item corresponding to the pushed subroutine, i.e. its offset in the list.
+ size_t registerSubroutine(eth::Assembly const& _assembly) { return size_t(m_asm.newSub(_assembly).data()); }
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
- /// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
- eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); }
+ /// on the stack. @returns the pushsub assembly item.
+ eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { auto sub = m_asm.newSub(_assembly); m_asm.append(m_asm.newPushSubSize(size_t(sub.data()))); return sub; }
+ void appendSubroutineSize(size_t const& _subRoutine) { m_asm.append(m_asm.newPushSubSize(_subRoutine)); }
/// Pushes the size of the final program
- void appendProgramSize() { return m_asm.appendProgramSize(); }
+ void appendProgramSize() { m_asm.appendProgramSize(); }
/// Adds data to the data section, pushes a reference to the stack
eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }
/// Appends the address (virtual, will be filled in by linker) of a library.
@@ -159,6 +157,11 @@ public:
void optimise(unsigned _runs = 200) { m_asm.optimise(true, true, _runs); }
+ /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
+ CompilerContext* runtimeContext() { return m_runtimeContext; }
+ /// @returns the identifier of the runtime subroutine.
+ size_t runtimeSub() const { return m_runtimeSub; }
+
eth::Assembly const& assembly() const { return m_asm; }
/// @returns non-const reference to the underlying assembly. Should be avoided in favour of
/// wrappers in this class.
@@ -185,9 +188,9 @@ public:
};
private:
- /// @returns the entry label of the given function - searches the inheritance hierarchy
- /// startig from the given point towards the base.
- eth::AssemblyItem virtualFunctionEntryLabel(
+ /// Searches the inheritance hierarchy towards the base starting from @a _searchStart and returns
+ /// the first function definition that is overwritten by _function.
+ FunctionDefinition const& resolveVirtualFunction(
FunctionDefinition const& _function,
std::vector<ContractDefinition const*>::const_iterator _searchStart
);
@@ -241,10 +244,10 @@ private:
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
/// Stack of current visited AST nodes, used for location attachment
std::stack<ASTNode const*> m_visitedNodes;
- /// The current mode of the compilation
- CompilationMode m_mode;
/// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime.
CompilerContext *m_runtimeContext;
+ /// The index of the runtime subroutine.
+ size_t m_runtimeSub = -1;
};
}
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index dedb53e7..1e21c020 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -340,6 +340,19 @@ void CompilerUtils::combineExternalFunctionType(bool _leftAligned)
m_context << Instruction::OR;
}
+void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
+{
+ m_context << m_context.functionEntryLabel(_function).pushTag();
+ // If there is a runtime context, we have to merge both labels into the same
+ // stack slot in case we store it in storage.
+ if (CompilerContext* rtc = m_context.runtimeContext())
+ m_context <<
+ (u256(1) << 32) <<
+ Instruction::MUL <<
+ rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) <<
+ Instruction::OR;
+}
+
void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
{
// For a type extension, we need to remove all higher-order bits that we might have ignored in
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 690452f9..2ebec81a 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -120,6 +120,10 @@ public:
void splitExternalFunctionType(bool _rightAligned);
/// Performs the opposite operation of splitExternalFunctionType(_rightAligned)
void combineExternalFunctionType(bool _rightAligned);
+ /// Appends code that combines the construction-time (if available) and runtime function
+ /// entry label of the given function into a single stack slot.
+ /// Note: This might cause the compilation queue of the runtime context to be extended.
+ void pushCombinedFunctionEntryLabel(Declaration const& _function);
/// Appends code for an implicit or explicit type conversion. This includes erasing higher
/// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 9cd893e8..79987af6 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -60,14 +60,13 @@ void ContractCompiler::compileContract(
}
size_t ContractCompiler::compileConstructor(
- CompilerContext const& _runtimeContext,
ContractDefinition const& _contract,
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
)
{
CompilerContext::LocationSetter locationSetter(m_context, _contract);
initializeContext(_contract, _contracts);
- return packIntoContractCreator(_contract, _runtimeContext);
+ return packIntoContractCreator(_contract);
}
size_t ContractCompiler::compileClone(
@@ -141,21 +140,31 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
appendBaseConstructor(*c);
}
-size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
+size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
{
+ solAssert(!!m_runtimeCompiler, "");
+
appendInitAndConstructorCode(_contract);
- eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.assembly());
+ // We jump to the deploy routine because we first have to append all missing functions,
+ // which can cause further functions to be added to the runtime context.
+ eth::AssemblyItem deployRoutine = m_context.appendJumpToNew();
+
+ // We have to include copies of functions in the construction time and runtime context
+ // because of absolute jumps.
+ appendMissingFunctions();
+ m_runtimeCompiler->appendMissingFunctions();
+
+ m_context << deployRoutine;
+
+ solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
+ m_context.appendSubroutineSize(m_context.runtimeSub());
// stack contains sub size
- m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY;
+ m_context << Instruction::DUP1 << m_context.runtimeSub() << u256(0) << Instruction::CODECOPY;
m_context << u256(0) << Instruction::RETURN;
- // note that we have to include the functions again because of absolute jump labels
- appendMissingFunctions();
-
- solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
- return size_t(runtimeSub.data());
+ return m_context.runtimeSub();
}
void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _constructor)
@@ -516,7 +525,19 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
{
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
- _assembly.append(m_context.virtualFunctionEntryLabel(*functionDef).pushTag());
+ {
+ functionDef = &m_context.resolveVirtualFunction(*functionDef);
+ _assembly.append(m_context.functionEntryLabel(*functionDef).pushTag());
+ // If there is a runtime context, we have to merge both labels into the same
+ // stack slot in case we store it in storage.
+ if (CompilerContext* rtc = m_context.runtimeContext())
+ {
+ _assembly.append(u256(1) << 32);
+ _assembly.append(Instruction::MUL);
+ _assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()));
+ _assembly.append(Instruction::OR);
+ }
+ }
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
{
solAssert(!variable->isConstant(), "");
diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h
index fecf6f5a..9e24523c 100644
--- a/libsolidity/codegen/ContractCompiler.h
+++ b/libsolidity/codegen/ContractCompiler.h
@@ -38,11 +38,12 @@ namespace solidity {
class ContractCompiler: private ASTConstVisitor
{
public:
- explicit ContractCompiler(CompilationMode _mode, CompilerContext* _runtimeContext, CompilerContext& _context, bool _optimise):
+ explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise):
m_optimise(_optimise),
+ m_runtimeCompiler(_runtimeCompiler),
m_context(_context)
{
- m_context = CompilerContext(_mode, _runtimeContext);
+ m_context = CompilerContext(_runtimeCompiler ? &_runtimeCompiler->m_context : nullptr);
}
void compileContract(
@@ -52,7 +53,6 @@ public:
/// Compiles the constructor part of the contract.
/// @returns the identifier of the runtime sub-assembly.
size_t compileConstructor(
- CompilerContext const& _runtimeContext,
ContractDefinition const& _contract,
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
);
@@ -74,7 +74,7 @@ private:
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context. Adds the constructor code.
/// @returns the identifier of the runtime sub assembly
- size_t packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
+ size_t packIntoContractCreator(ContractDefinition const& _contract);
/// Appends state variable initialisation and constructor code.
void appendInitAndConstructorCode(ContractDefinition const& _contract);
void appendBaseConstructor(FunctionDefinition const& _constructor);
@@ -117,6 +117,8 @@ private:
static eth::Assembly cloneRuntime();
bool const m_optimise;
+ /// Pointer to the runtime compiler in case this is a creation compiler.
+ ContractCompiler* m_runtimeCompiler = nullptr;
CompilerContext& m_context;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index e3f05c21..83c3a2c4 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -488,6 +488,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
parameterSize += function.selfType()->sizeOnStack();
}
+ if (m_context.runtimeContext())
+ // We have a runtime context, so we need the creation part.
+ m_context << (u256(1) << 32) << Instruction::SWAP1 << Instruction::DIV;
+ else
+ // Extract the runtime part.
+ m_context << ((u256(1) << 32) - 1) << Instruction::AND;
+
m_context.appendJump(eth::AssemblyItem::JumpType::IntoFunction);
m_context << returnLabel;
@@ -845,9 +852,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
);
if (funType->location() == FunctionType::Location::Internal)
{
- m_context << m_context.functionEntryLabel(
- dynamic_cast<FunctionDefinition const&>(funType->declaration())
- ).pushTag();
+ FunctionDefinition const& funDef = dynamic_cast<decltype(funDef)>(funType->declaration());
+ utils().pushCombinedFunctionEntryLabel(funDef);
utils().moveIntoStack(funType->selfType()->sizeOnStack(), 1);
}
else
@@ -883,7 +889,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
// us to link against it although we actually do not need it.
auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
solAssert(!!function, "Function not found in member access");
- m_context << m_context.functionEntryLabel(*function).pushTag();
+ utils().pushCombinedFunctionEntryLabel(*function);
}
}
else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type.get()))
@@ -915,10 +921,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
if (type.isSuper())
{
solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
- m_context << m_context.superFunctionEntryLabel(
+ utils().pushCombinedFunctionEntryLabel(m_context.superFunction(
dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
type.contractDefinition()
- ).pushTag();
+ ));
}
else
{
@@ -1203,7 +1209,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
}
}
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
- m_context << m_context.virtualFunctionEntryLabel(*functionDef).pushTag();
+ utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef));
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
appendVariable(*variable, static_cast<Expression const&>(_identifier));
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
@@ -1266,6 +1272,17 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
{
if (_operator == Token::Equal || _operator == Token::NotEqual)
{
+ if (FunctionType const* funType = dynamic_cast<decltype(funType)>(&_type))
+ {
+ if (funType->location() == FunctionType::Location::Internal)
+ {
+ // We have to remove the upper bits (construction time value) because they might
+ // be "unknown" in one of the operands and not in the other.
+ m_context << ((u256(1) << 32) - 1) << Instruction::AND;
+ m_context << Instruction::SWAP1;
+ m_context << ((u256(1) << 32) - 1) << Instruction::AND;
+ }
+ }
m_context << Instruction::EQ;
if (_operator == Token::NotEqual)
m_context << Instruction::ISZERO;