diff options
author | chriseth <chris@ethereum.org> | 2017-05-27 01:30:42 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-27 01:30:42 +0800 |
commit | 788b64ea6181af6e7e364e86d64508c4809ca9b7 (patch) | |
tree | 2a136a342d6f8980f96043b0f6ce74828f89fdd6 | |
parent | e022f11cdb752a07b6c0f824f3e4f91233a19359 (diff) | |
parent | fe32531a16322c9528c34cfbd32924b4c24a1f13 (diff) | |
download | dexon-solidity-788b64ea6181af6e7e364e86d64508c4809ca9b7.tar.gz dexon-solidity-788b64ea6181af6e7e364e86d64508c4809ca9b7.tar.zst dexon-solidity-788b64ea6181af6e7e364e86d64508c4809ca9b7.zip |
Merge pull request #2291 from ethereum/evm15
Allow different assembly types and target machines.
-rw-r--r-- | libjulia/backends/evm/AbstractAssembly.h (renamed from libjulia/backends/AbstractAssembly.h) | 0 | ||||
-rw-r--r-- | libjulia/backends/evm/EVMCodeTransform.cpp | 253 | ||||
-rw-r--r-- | libjulia/backends/evm/EVMCodeTransform.h | 119 | ||||
-rw-r--r-- | libsolidity/CMakeLists.txt | 4 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 4 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 10 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.cpp | 10 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 8 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.cpp | 6 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.h | 4 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmCodeGen.cpp | 260 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmCodeGen.h | 4 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmStack.cpp | 4 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmStack.h | 23 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.cpp | 85 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.h | 85 | ||||
-rw-r--r-- | solc/CommandLineInterface.cpp | 111 | ||||
-rw-r--r-- | solc/CommandLineInterface.h | 9 |
18 files changed, 667 insertions, 332 deletions
diff --git a/libjulia/backends/AbstractAssembly.h b/libjulia/backends/evm/AbstractAssembly.h index e3afa2b6..e3afa2b6 100644 --- a/libjulia/backends/AbstractAssembly.h +++ b/libjulia/backends/evm/AbstractAssembly.h diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp new file mode 100644 index 00000000..18c3e63c --- /dev/null +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -0,0 +1,253 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Common code generator for translating Julia / inline assembly to EVM and EVM1.5. + */ + +#include <libjulia/backends/evm/EVMCodeTransform.h> + +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libsolidity/inlineasm/AsmData.h> + +#include <libsolidity/interface/Utils.h> + +using namespace std; +using namespace dev; +using namespace dev::julia; +using namespace dev::solidity; +using namespace dev::solidity::assembly; + +CodeTransform::CodeTransform( + ErrorList& _errors, + AbstractAssembly& _assembly, + Block const& _block, + AsmAnalysisInfo& _analysisInfo, + ExternalIdentifierAccess const& _identifierAccess, + int _initialStackHeight +): + m_errors(_errors), + m_assembly(_assembly), + m_info(_analysisInfo), + m_scope(*_analysisInfo.scopes.at(&_block)), + m_identifierAccess(_identifierAccess), + m_initialStackHeight(_initialStackHeight) +{ + int blockStartStackHeight = m_assembly.stackHeight(); + std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this)); + + m_assembly.setSourceLocation(_block.location); + + // pop variables + for (auto const& identifier: m_scope.identifiers) + if (identifier.second.type() == typeid(Scope::Variable)) + m_assembly.appendInstruction(solidity::Instruction::POP); + + int deposit = m_assembly.stackHeight() - blockStartStackHeight; + solAssert(deposit == 0, "Invalid stack height at end of block."); +} + +void CodeTransform::operator()(const FunctionDefinition&) +{ + solAssert(false, "Function definition not removed during desugaring phase."); +} + +void CodeTransform::generateAssignment(Identifier const& _variableName, SourceLocation const& _location) +{ + auto var = m_scope.lookup(_variableName.name); + if (var) + { + Scope::Variable const& _var = boost::get<Scope::Variable>(*var); + if (int heightDiff = variableHeightDiff(_var, _location, true)) + m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); + m_assembly.appendInstruction(solidity::Instruction::POP); + } + else + { + solAssert( + m_identifierAccess.generateCode, + "Identifier not found and no external access available." + ); + m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_assembly); + } +} + +int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, SourceLocation const& _location, bool _forSwap) +{ + int heightDiff = m_assembly.stackHeight() - _var.stackHeight; + if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16)) + { + //@TODO move this to analysis phase. + m_errors.push_back(make_shared<Error>( + Error::Type::TypeError, + "Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")", + _location + )); + return 0; + } + else + return heightDiff; +} + +void CodeTransform::expectDeposit(int _deposit, int _oldHeight) +{ + solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); +} + +void CodeTransform::checkStackHeight(void const* _astElement) +{ + solAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); + solAssert( + m_info.stackHeightInfo.at(_astElement) == m_assembly.stackHeight() - m_initialStackHeight, + "Stack height mismatch between analysis and code generation phase." + ); +} + +void CodeTransform::assignLabelIdIfUnset(Scope::Label& _label) +{ + if (!_label.id) + _label.id.reset(m_assembly.newLabelId()); +} + +void CodeTransform::operator()(Block const& _block) +{ + CodeTransform(m_errors, m_assembly, _block, m_info, m_identifierAccess, m_initialStackHeight); + checkStackHeight(&_block); +} + +void CodeTransform::operator()(Switch const&) +{ + solAssert(false, "Switch not removed during desugaring phase."); +} + +void CodeTransform::operator()(VariableDeclaration const& _varDecl) +{ + int expectedItems = _varDecl.variables.size(); + int height = m_assembly.stackHeight(); + boost::apply_visitor(*this, *_varDecl.value); + expectDeposit(expectedItems, height); + for (auto const& variable: _varDecl.variables) + { + auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(variable.name)); + var.stackHeight = height++; + var.active = true; + } +} + +void CodeTransform::operator()(Assignment const& _assignment) +{ + int height = m_assembly.stackHeight(); + boost::apply_visitor(*this, *_assignment.value); + expectDeposit(1, height); + m_assembly.setSourceLocation(_assignment.location); + generateAssignment(_assignment.variableName, _assignment.location); + checkStackHeight(&_assignment); +} + +void CodeTransform::operator()(StackAssignment const& _assignment) +{ + m_assembly.setSourceLocation(_assignment.location); + generateAssignment(_assignment.variableName, _assignment.location); + checkStackHeight(&_assignment); +} + +void CodeTransform::operator()(Label const& _label) +{ + m_assembly.setSourceLocation(_label.location); + solAssert(m_scope.identifiers.count(_label.name), ""); + Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers.at(_label.name)); + assignLabelIdIfUnset(label); + m_assembly.appendLabel(*label.id); + checkStackHeight(&_label); +} + +void CodeTransform::operator()(FunctionCall const&) +{ + solAssert(false, "Function call not removed during desugaring phase."); +} + +void CodeTransform::operator()(FunctionalInstruction const& _instr) +{ + for (auto it = _instr.arguments.rbegin(); it != _instr.arguments.rend(); ++it) + { + int height = m_assembly.stackHeight(); + boost::apply_visitor(*this, *it); + expectDeposit(1, height); + } + (*this)(_instr.instruction); + checkStackHeight(&_instr); +} + +void CodeTransform::operator()(assembly::Identifier const& _identifier) +{ + m_assembly.setSourceLocation(_identifier.location); + // First search internals, then externals. + if (m_scope.lookup(_identifier.name, Scope::NonconstVisitor( + [=](Scope::Variable& _var) + { + if (int heightDiff = variableHeightDiff(_var, _identifier.location, false)) + m_assembly.appendInstruction(solidity::dupInstruction(heightDiff)); + else + // Store something to balance the stack + m_assembly.appendConstant(u256(0)); + }, + [=](Scope::Label& _label) + { + assignLabelIdIfUnset(_label); + m_assembly.appendLabelReference(*_label.id); + }, + [=](Scope::Function&) + { + solAssert(false, "Function not removed during desugaring."); + } + ))) + { + return; + } + solAssert( + m_identifierAccess.generateCode, + "Identifier not found and no external access available." + ); + m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_assembly); + checkStackHeight(&_identifier); +} + +void CodeTransform::operator()(assembly::Literal const& _literal) +{ + m_assembly.setSourceLocation(_literal.location); + if (_literal.kind == assembly::LiteralKind::Number) + m_assembly.appendConstant(u256(_literal.value)); + else if (_literal.kind == assembly::LiteralKind::Boolean) + { + if (_literal.value == "true") + m_assembly.appendConstant(u256(1)); + else + m_assembly.appendConstant(u256(0)); + } + else + { + solAssert(_literal.value.size() <= 32, ""); + m_assembly.appendConstant(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft))); + } + checkStackHeight(&_literal); +} + +void CodeTransform::operator()(assembly::Instruction const& _instruction) +{ + m_assembly.setSourceLocation(_instruction.location); + m_assembly.appendInstruction(_instruction.instruction); + checkStackHeight(&_instruction); +} diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h new file mode 100644 index 00000000..5408a3aa --- /dev/null +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -0,0 +1,119 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Common code generator for translating Julia / inline assembly to EVM and EVM1.5. + */ + +#include <libjulia/backends/evm/AbstractAssembly.h> + +#include <libsolidity/interface/Exceptions.h> + +#include <libsolidity/inlineasm/AsmStack.h> +#include <libsolidity/inlineasm/AsmScope.h> + +#include <boost/variant.hpp> + +namespace dev +{ +namespace solidity +{ +namespace assembly +{ +struct Literal; +struct Block; +struct Switch; +struct Label; +struct FunctionalInstruction; +struct Assignment; +struct VariableDeclaration; +struct Instruction; +struct Identifier; +struct StackAssignment; +struct FunctionDefinition; +struct FunctionCall; + +struct AsmAnalysisInfo; +} +} +namespace julia +{ + +class CodeTransform: public boost::static_visitor<> +{ +public: + /// Create the code transformer which appends assembly to _assembly as a side-effect + /// of its creation. + /// @param _identifierAccess used to resolve identifiers external to the inline assembly + CodeTransform( + solidity::ErrorList& _errors, + julia::AbstractAssembly& _assembly, + solidity::assembly::Block const& _block, + solidity::assembly::AsmAnalysisInfo& _analysisInfo, + ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess() + ): CodeTransform(_errors, _assembly, _block, _analysisInfo, _identifierAccess, _assembly.stackHeight()) + { + } + +private: + CodeTransform( + solidity::ErrorList& _errors, + julia::AbstractAssembly& _assembly, + solidity::assembly::Block const& _block, + solidity::assembly::AsmAnalysisInfo& _analysisInfo, + ExternalIdentifierAccess const& _identifierAccess, + int _initialStackHeight + ); + +public: + void operator()(solidity::assembly::Instruction const& _instruction); + void operator()(solidity::assembly::Literal const& _literal); + void operator()(solidity::assembly::Identifier const& _identifier); + void operator()(solidity::assembly::FunctionalInstruction const& _instr); + void operator()(solidity::assembly::FunctionCall const&); + void operator()(solidity::assembly::Label const& _label); + void operator()(solidity::assembly::StackAssignment const& _assignment); + void operator()(solidity::assembly::Assignment const& _assignment); + void operator()(solidity::assembly::VariableDeclaration const& _varDecl); + void operator()(solidity::assembly::Block const& _block); + void operator()(solidity::assembly::Switch const& _switch); + void operator()(solidity::assembly::FunctionDefinition const&); + +private: + void generateAssignment(solidity::assembly::Identifier const& _variableName, SourceLocation const& _location); + + /// Determines the stack height difference to the given variables. Automatically generates + /// errors if it is not yet in scope or the height difference is too large. Returns 0 on + /// errors and the (positive) stack height difference otherwise. + int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, SourceLocation const& _location, bool _forSwap); + + void expectDeposit(int _deposit, int _oldHeight); + + void checkStackHeight(void const* _astElement); + + /// Assigns the label's id to a value taken from eth::Assembly if it has not yet been set. + void assignLabelIdIfUnset(solidity::assembly::Scope::Label& _label); + + solidity::ErrorList& m_errors; + julia::AbstractAssembly& m_assembly; + solidity::assembly::AsmAnalysisInfo& m_info; + solidity::assembly::Scope& m_scope; + ExternalIdentifierAccess m_identifierAccess; + int const m_initialStackHeight; +}; + +} +} diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index bcc47e5a..2342f0f9 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -7,10 +7,12 @@ aux_source_directory(formal SRC_LIST) aux_source_directory(interface SRC_LIST) aux_source_directory(parsing SRC_LIST) aux_source_directory(inlineasm SRC_LIST) +# Until we have a clear separation, libjulia has to be included here +aux_source_directory(../libjulia SRC_LIST) set(EXECUTABLE solidity) -file(GLOB HEADERS "*/*.h") +file(GLOB HEADERS "*/*.h" "../libjulia/backends/evm/*") include_directories(BEFORE ..) add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 9433976a..83ea17c7 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -166,8 +166,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // The only purpose of this step is to fill the inline assembly annotation with // external references. ErrorList errorsIgnored; - assembly::ExternalIdentifierAccess::Resolver resolver = - [&](assembly::Identifier const& _identifier, assembly::IdentifierContext) { + julia::ExternalIdentifierAccess::Resolver resolver = + [&](assembly::Identifier const& _identifier, julia::IdentifierContext) { auto declarations = m_resolver.nameFromCurrentScope(_identifier.name); bool isSlot = boost::algorithm::ends_with(_identifier.name, "_slot"); bool isOffset = boost::algorithm::ends_with(_identifier.name, "_offset"); diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8161a3a1..ad22cc66 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -631,9 +631,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { // External references have already been resolved in a prior stage and stored in the annotation. // We run the resolve step again regardless. - assembly::ExternalIdentifierAccess::Resolver identifierAccess = [&]( + julia::ExternalIdentifierAccess::Resolver identifierAccess = [&]( assembly::Identifier const& _identifier, - assembly::IdentifierContext _context + julia::IdentifierContext _context ) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); @@ -650,7 +650,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); return size_t(-1); } - else if (_context != assembly::IdentifierContext::RValue) + else if (_context != julia::IdentifierContext::RValue) { typeError(_identifier.location, "Storage variables cannot be assigned to."); return size_t(-1); @@ -677,13 +677,13 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) return size_t(-1); } } - else if (_context == assembly::IdentifierContext::LValue) + else if (_context == julia::IdentifierContext::LValue) { typeError(_identifier.location, "Only local variables can be assigned to in inline assembly."); return size_t(-1); } - if (_context == assembly::IdentifierContext::RValue) + if (_context == julia::IdentifierContext::RValue) { solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); if (dynamic_cast<FunctionDefinition const*>(declaration)) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 5c4f88c4..404a3af6 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -266,10 +266,10 @@ void CompilerContext::appendInlineAssembly( int startStackHeight = stackHeight(); - assembly::ExternalIdentifierAccess identifierAccess; + julia::ExternalIdentifierAccess identifierAccess; identifierAccess.resolve = [&]( assembly::Identifier const& _identifier, - assembly::IdentifierContext + julia::IdentifierContext ) { auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name); @@ -277,7 +277,7 @@ void CompilerContext::appendInlineAssembly( }; identifierAccess.generateCode = [&]( assembly::Identifier const& _identifier, - assembly::IdentifierContext _context, + julia::IdentifierContext _context, julia::AbstractAssembly& _assembly ) { @@ -285,14 +285,14 @@ void CompilerContext::appendInlineAssembly( solAssert(it != _localVariables.end(), ""); int stackDepth = _localVariables.end() - it; int stackDiff = _assembly.stackHeight() - startStackHeight + stackDepth; - if (_context == assembly::IdentifierContext::LValue) + if (_context == julia::IdentifierContext::LValue) stackDiff -= 1; if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( CompilerError() << errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.") ); - if (_context == assembly::IdentifierContext::RValue) + if (_context == julia::IdentifierContext::RValue) _assembly.appendInstruction(dupInstruction(stackDiff)); else { diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 41940e1a..e79bb6dc 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -522,21 +522,21 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) ErrorList errors; assembly::CodeGenerator codeGen(errors); unsigned startStackHeight = m_context.stackHeight(); - assembly::ExternalIdentifierAccess identifierAccess; - identifierAccess.resolve = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext) + julia::ExternalIdentifierAccess identifierAccess; + identifierAccess.resolve = [&](assembly::Identifier const& _identifier, julia::IdentifierContext) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); if (ref == _inlineAssembly.annotation().externalReferences.end()) return size_t(-1); return ref->second.valueSize; }; - identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext _context, julia::AbstractAssembly& _assembly) + identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, julia::IdentifierContext _context, julia::AbstractAssembly& _assembly) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), ""); Declaration const* decl = ref->second.declaration; solAssert(!!decl, ""); - if (_context == assembly::IdentifierContext::RValue) + if (_context == julia::IdentifierContext::RValue) { int const depositBefore = _assembly.stackHeight(); solAssert(!!decl->type(), "Type of declaration required but not yet determined."); diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index d022ecf6..60caaa1d 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -41,7 +41,7 @@ using namespace dev::solidity::assembly; AsmAnalyzer::AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, ErrorList& _errors, - ExternalIdentifierAccess::Resolver const& _resolver + julia::ExternalIdentifierAccess::Resolver const& _resolver ): m_resolver(_resolver), m_info(_analysisInfo), m_errors(_errors) { @@ -123,7 +123,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) { size_t stackSize(-1); if (m_resolver) - stackSize = m_resolver(_identifier, IdentifierContext::RValue); + stackSize = m_resolver(_identifier, julia::IdentifierContext::RValue); if (stackSize == size_t(-1)) { // Only add an error message if the callback did not do it. @@ -393,7 +393,7 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t variableSize = 1; } else if (m_resolver) - variableSize = m_resolver(_variable, IdentifierContext::LValue); + variableSize = m_resolver(_variable, julia::IdentifierContext::LValue); if (variableSize == size_t(-1)) { // Only add message if the callback did not. diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index 9f022b12..613abf38 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -64,7 +64,7 @@ public: AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, ErrorList& _errors, - ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver() + julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() ); bool analyze(assembly::Block const& _block); @@ -94,7 +94,7 @@ private: /// we enter the block. int m_virtualVariablesInNextBlock = 0; int m_stackHeight = 0; - ExternalIdentifierAccess::Resolver const& m_resolver; + julia::ExternalIdentifierAccess::Resolver const& m_resolver; Scope* m_currentScope = nullptr; AsmAnalysisInfo& m_info; ErrorList& m_errors; diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 5c66b125..6a44faac 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -32,7 +32,8 @@ #include <libevmasm/SourceLocation.h> #include <libevmasm/Instruction.h> -#include <libjulia/backends/AbstractAssembly.h> +#include <libjulia/backends/evm/AbstractAssembly.h> +#include <libjulia/backends/evm/EVMCodeTransform.h> #include <libdevcore/CommonIO.h> @@ -48,15 +49,6 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::assembly; -struct GeneratorState -{ - GeneratorState(ErrorList& _errors, AsmAnalysisInfo& _analysisInfo): - errors(_errors), info(_analysisInfo) {} - - ErrorList& errors; - AsmAnalysisInfo info; -}; - class EthAssemblyAdapter: public julia::AbstractAssembly { public: @@ -107,254 +99,15 @@ private: eth::Assembly& m_assembly; }; -class CodeTransform: public boost::static_visitor<> -{ -public: - /// Create the code transformer which appends assembly to _state.assembly when called - /// with parsed assembly data. - /// @param _identifierAccess used to resolve identifiers external to the inline assembly - explicit CodeTransform( - GeneratorState& _state, - julia::AbstractAssembly& _assembly, - assembly::Block const& _block, - assembly::ExternalIdentifierAccess const& _identifierAccess = assembly::ExternalIdentifierAccess() - ): CodeTransform(_state, _assembly, _block, _identifierAccess, _assembly.stackHeight()) - { - } - -private: - CodeTransform( - GeneratorState& _state, - julia::AbstractAssembly& _assembly, - assembly::Block const& _block, - assembly::ExternalIdentifierAccess const& _identifierAccess, - int _initialStackHeight - ): - m_state(_state), - m_assembly(_assembly), - m_scope(*m_state.info.scopes.at(&_block)), - m_identifierAccess(_identifierAccess), - m_initialStackHeight(_initialStackHeight) - { - int blockStartStackHeight = m_assembly.stackHeight(); - std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this)); - - m_assembly.setSourceLocation(_block.location); - - // pop variables - for (auto const& identifier: m_scope.identifiers) - if (identifier.second.type() == typeid(Scope::Variable)) - m_assembly.appendInstruction(solidity::Instruction::POP); - - int deposit = m_assembly.stackHeight() - blockStartStackHeight; - solAssert(deposit == 0, "Invalid stack height at end of block."); - } - -public: - void operator()(assembly::Instruction const& _instruction) - { - m_assembly.setSourceLocation(_instruction.location); - m_assembly.appendInstruction(_instruction.instruction); - checkStackHeight(&_instruction); - } - void operator()(assembly::Literal const& _literal) - { - m_assembly.setSourceLocation(_literal.location); - if (_literal.kind == assembly::LiteralKind::Number) - m_assembly.appendConstant(u256(_literal.value)); - else if (_literal.kind == assembly::LiteralKind::Boolean) - { - if (_literal.value == "true") - m_assembly.appendConstant(u256(1)); - else - m_assembly.appendConstant(u256(0)); - } - else - { - solAssert(_literal.value.size() <= 32, ""); - m_assembly.appendConstant(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft))); - } - checkStackHeight(&_literal); - } - void operator()(assembly::Identifier const& _identifier) - { - m_assembly.setSourceLocation(_identifier.location); - // First search internals, then externals. - if (m_scope.lookup(_identifier.name, Scope::NonconstVisitor( - [=](Scope::Variable& _var) - { - if (int heightDiff = variableHeightDiff(_var, _identifier.location, false)) - m_assembly.appendInstruction(solidity::dupInstruction(heightDiff)); - else - // Store something to balance the stack - m_assembly.appendConstant(u256(0)); - }, - [=](Scope::Label& _label) - { - assignLabelIdIfUnset(_label); - m_assembly.appendLabelReference(*_label.id); - }, - [=](Scope::Function&) - { - solAssert(false, "Function not removed during desugaring."); - } - ))) - { - return; - } - solAssert( - m_identifierAccess.generateCode, - "Identifier not found and no external access available." - ); - m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_assembly); - checkStackHeight(&_identifier); - } - void operator()(FunctionalInstruction const& _instr) - { - for (auto it = _instr.arguments.rbegin(); it != _instr.arguments.rend(); ++it) - { - int height = m_assembly.stackHeight(); - boost::apply_visitor(*this, *it); - expectDeposit(1, height); - } - (*this)(_instr.instruction); - checkStackHeight(&_instr); - } - void operator()(assembly::FunctionCall const&) - { - solAssert(false, "Function call not removed during desugaring phase."); - } - void operator()(Label const& _label) - { - m_assembly.setSourceLocation(_label.location); - solAssert(m_scope.identifiers.count(_label.name), ""); - Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers.at(_label.name)); - assignLabelIdIfUnset(label); - m_assembly.appendLabel(*label.id); - checkStackHeight(&_label); - } - void operator()(assembly::StackAssignment const& _assignment) - { - m_assembly.setSourceLocation(_assignment.location); - generateAssignment(_assignment.variableName, _assignment.location); - checkStackHeight(&_assignment); - } - void operator()(assembly::Assignment const& _assignment) - { - int height = m_assembly.stackHeight(); - boost::apply_visitor(*this, *_assignment.value); - expectDeposit(1, height); - m_assembly.setSourceLocation(_assignment.location); - generateAssignment(_assignment.variableName, _assignment.location); - checkStackHeight(&_assignment); - } - void operator()(assembly::VariableDeclaration const& _varDecl) - { - int height = m_assembly.stackHeight(); - int expectedItems = _varDecl.variables.size(); - boost::apply_visitor(*this, *_varDecl.value); - expectDeposit(expectedItems, height); - for (auto const& variable: _varDecl.variables) - { - auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(variable.name)); - var.stackHeight = height++; - var.active = true; - } - } - void operator()(assembly::Block const& _block) - { - CodeTransform(m_state, m_assembly, _block, m_identifierAccess, m_initialStackHeight); - checkStackHeight(&_block); - } - void operator()(assembly::Switch const&) - { - solAssert(false, "Switch not removed during desugaring phase."); - } - void operator()(assembly::FunctionDefinition const&) - { - solAssert(false, "Function definition not removed during desugaring phase."); - } - -private: - void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location) - { - auto var = m_scope.lookup(_variableName.name); - if (var) - { - Scope::Variable const& _var = boost::get<Scope::Variable>(*var); - if (int heightDiff = variableHeightDiff(_var, _location, true)) - m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); - m_assembly.appendInstruction(solidity::Instruction::POP); - } - else - { - solAssert( - m_identifierAccess.generateCode, - "Identifier not found and no external access available." - ); - m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_assembly); - } - } - - /// Determines the stack height difference to the given variables. Automatically generates - /// errors if it is not yet in scope or the height difference is too large. Returns 0 on - /// errors and the (positive) stack height difference otherwise. - int variableHeightDiff(Scope::Variable const& _var, SourceLocation const& _location, bool _forSwap) - { - int heightDiff = m_assembly.stackHeight() - _var.stackHeight; - if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16)) - { - //@TODO move this to analysis phase. - m_state.errors.push_back(make_shared<Error>( - Error::Type::TypeError, - "Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")", - _location - )); - return 0; - } - else - return heightDiff; - } - - void expectDeposit(int _deposit, int _oldHeight) - { - solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); - } - - void checkStackHeight(void const* _astElement) - { - solAssert(m_state.info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); - solAssert( - m_state.info.stackHeightInfo.at(_astElement) == m_assembly.stackHeight() - m_initialStackHeight, - "Stack height mismatch between analysis and code generation phase." - ); - } - - /// Assigns the label's id to a value taken from eth::Assembly if it has not yet been set. - void assignLabelIdIfUnset(Scope::Label& _label) - { - if (!_label.id) - _label.id.reset(m_assembly.newLabelId()); - } - - - GeneratorState& m_state; - julia::AbstractAssembly& m_assembly; - Scope& m_scope; - ExternalIdentifierAccess m_identifierAccess; - int const m_initialStackHeight; -}; - eth::Assembly assembly::CodeGenerator::assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, - ExternalIdentifierAccess const& _identifierAccess + julia::ExternalIdentifierAccess const& _identifierAccess ) { eth::Assembly assembly; - GeneratorState state(m_errors, _analysisInfo); EthAssemblyAdapter assemblyAdapter(assembly); - CodeTransform(state, assemblyAdapter, _parsedData, _identifierAccess); + julia::CodeTransform(m_errors, assemblyAdapter, _parsedData, _analysisInfo, _identifierAccess); return assembly; } @@ -362,10 +115,9 @@ void assembly::CodeGenerator::assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - ExternalIdentifierAccess const& _identifierAccess + julia::ExternalIdentifierAccess const& _identifierAccess ) { - GeneratorState state(m_errors, _analysisInfo); EthAssemblyAdapter assemblyAdapter(_assembly); - CodeTransform(state, assemblyAdapter, _parsedData, _identifierAccess); + julia::CodeTransform(m_errors, assemblyAdapter, _parsedData, _analysisInfo, _identifierAccess); } diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h index e830e047..1b43d2f6 100644 --- a/libsolidity/inlineasm/AsmCodeGen.h +++ b/libsolidity/inlineasm/AsmCodeGen.h @@ -48,14 +48,14 @@ public: eth::Assembly assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, - ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess() + julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess() ); /// Performs code generation and appends generated to to _assembly. void assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess() + julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess() ); private: diff --git a/libsolidity/inlineasm/AsmStack.cpp b/libsolidity/inlineasm/AsmStack.cpp index c2a7d8ea..2e63465f 100644 --- a/libsolidity/inlineasm/AsmStack.cpp +++ b/libsolidity/inlineasm/AsmStack.cpp @@ -42,7 +42,7 @@ using namespace dev::solidity::assembly; bool InlineAssemblyStack::parse( shared_ptr<Scanner> const& _scanner, - ExternalIdentifierAccess::Resolver const& _resolver + julia::ExternalIdentifierAccess::Resolver const& _resolver ) { m_parserResult = make_shared<Block>(); @@ -73,7 +73,7 @@ eth::Assembly InlineAssemblyStack::assemble() bool InlineAssemblyStack::parseAndAssemble( string const& _input, eth::Assembly& _assembly, - ExternalIdentifierAccess const& _identifierAccess + julia::ExternalIdentifierAccess const& _identifierAccess ) { ErrorList errors; diff --git a/libsolidity/inlineasm/AsmStack.h b/libsolidity/inlineasm/AsmStack.h index e223ccc9..23072a88 100644 --- a/libsolidity/inlineasm/AsmStack.h +++ b/libsolidity/inlineasm/AsmStack.h @@ -24,7 +24,7 @@ #include <libsolidity/interface/Exceptions.h> -#include <libjulia/backends/AbstractAssembly.h> +#include <libjulia/backends/evm/AbstractAssembly.h> #include <string> #include <functional> @@ -43,23 +43,6 @@ namespace assembly struct Block; struct Identifier; -enum class IdentifierContext { LValue, RValue }; - -/// Object that is used to resolve references and generate code for access to identifiers external -/// to inline assembly (not used in standalone assembly mode). -struct ExternalIdentifierAccess -{ - using Resolver = std::function<size_t(assembly::Identifier const&, IdentifierContext)>; - /// Resolve a an external reference given by the identifier in the given context. - /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. - Resolver resolve; - using CodeGenerator = std::function<void(assembly::Identifier const&, IdentifierContext, julia::AbstractAssembly&)>; - /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context) - /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed - /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack. - CodeGenerator generateCode; -}; - class InlineAssemblyStack { public: @@ -67,7 +50,7 @@ public: /// @return false or error. bool parse( std::shared_ptr<Scanner> const& _scanner, - ExternalIdentifierAccess::Resolver const& _externalIdentifierResolver = ExternalIdentifierAccess::Resolver() + julia::ExternalIdentifierAccess::Resolver const& _externalIdentifierResolver = julia::ExternalIdentifierAccess::Resolver() ); /// Converts the parser result back into a string form (not necessarily the same form /// as the source form, but it should parse into the same parsed form again). @@ -79,7 +62,7 @@ public: bool parseAndAssemble( std::string const& _input, eth::Assembly& _assembly, - ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess() + julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess() ); ErrorList const& errors() const { return m_errors; } diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp new file mode 100644 index 00000000..c4bd63c4 --- /dev/null +++ b/libsolidity/interface/AssemblyStack.cpp @@ -0,0 +1,85 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * eWasm as output. + */ + + +#include <libsolidity/interface/AssemblyStack.h> + +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmCodeGen.h> + +#include <libevmasm/Assembly.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + + +Scanner const& AssemblyStack::scanner() const +{ + solAssert(m_scanner, ""); + return *m_scanner; +} + +bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source) +{ + m_analysisSuccessful = false; + m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName); + m_parserResult = assembly::Parser(m_errors, m_language == Language::JULIA).parse(m_scanner); + if (!m_errors.empty()) + return false; + solAssert(m_parserResult, ""); + + m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errors); + m_analysisSuccessful = analyzer.analyze(*m_parserResult); + return m_analysisSuccessful; +} + +eth::LinkerObject AssemblyStack::assemble(Machine _machine) +{ + solAssert(m_analysisSuccessful, ""); + solAssert(m_parserResult, ""); + solAssert(m_analysisInfo, ""); + + switch (_machine) + { + case Machine::EVM: + { + auto assembly = assembly::CodeGenerator(m_errors).assemble(*m_parserResult, *m_analysisInfo); + return assembly.assemble(); + } + case Machine::EVM15: + solUnimplemented("EVM 1.5 backend is not yet implemented."); + case Machine::eWasm: + solUnimplemented("eWasm backend is not yet implemented."); + } + // unreachable + return eth::LinkerObject(); +} + +string AssemblyStack::print() +{ + solAssert(m_parserResult, ""); + return assembly::AsmPrinter(m_language == Language::JULIA)(*m_parserResult); +} diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h new file mode 100644 index 00000000..40662ac3 --- /dev/null +++ b/libsolidity/interface/AssemblyStack.h @@ -0,0 +1,85 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * eWasm as output. + */ + +#pragma once + +#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libevmasm/LinkerObject.h> + +#include <string> +#include <memory> + +namespace dev +{ +namespace solidity +{ +class Scanner; +namespace assembly +{ +struct AsmAnalysisInfo; +struct Block; +} + +/* + * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * eWasm as output. + */ +class AssemblyStack +{ +public: + enum class Language { JULIA, Assembly }; + enum class Machine { EVM, EVM15, eWasm }; + + explicit AssemblyStack(Language _language = Language::Assembly): + m_language(_language) + {} + + /// @returns the scanner used during parsing + Scanner const& scanner() const; + + /// Runs parsing and analysis steps, returns false if input cannot be assembled. + /// Multiple calls overwrite the previous state. + bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source); + + /// Run the assembly step (should only be called after parseAndAnalyze). + eth::LinkerObject assemble(Machine _machine); + + /// @returns the errors generated during parsing, analysis (and potentially assembly). + ErrorList const& errors() const { return m_errors; } + + /// Pretty-print the input after having parsed it. + std::string print(); + +private: + + Language m_language = Language::Assembly; + + std::shared_ptr<Scanner> m_scanner; + + bool m_analysisSuccessful = false; + std::shared_ptr<assembly::Block> m_parserResult; + std::shared_ptr<assembly::AsmAnalysisInfo> m_analysisInfo; + ErrorList m_errors; +}; + +} +} diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e55e3e7e..c14a46e8 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -35,6 +35,7 @@ #include <libsolidity/interface/StandardCompiler.h> #include <libsolidity/interface/SourceReferenceFormatter.h> #include <libsolidity/interface/GasEstimator.h> +#include <libsolidity/interface/AssemblyStack.h> #include <libsolidity/formal/Why3Translator.h> #include <libevmasm/Instruction.h> @@ -80,11 +81,15 @@ static string const g_strBinaryRuntime = "bin-runtime"; static string const g_strCloneBinary = "clone-bin"; static string const g_strCombinedJson = "combined-json"; static string const g_strContracts = "contracts"; +static string const g_strEVM = "evm"; +static string const g_strEVM15 = "evm15"; +static string const g_streWasm = "ewasm"; static string const g_strFormal = "formal"; static string const g_strGas = "gas"; static string const g_strHelp = "help"; static string const g_strInputFile = "input-file"; static string const g_strInterface = "interface"; +static string const g_strJulia = "julia"; static string const g_strLibraries = "libraries"; static string const g_strLink = "link"; static string const g_strMetadata = "metadata"; @@ -121,8 +126,10 @@ static string const g_argFormal = g_strFormal; static string const g_argGas = g_strGas; static string const g_argHelp = g_strHelp; static string const g_argInputFile = g_strInputFile; +static string const g_argJulia = "julia"; static string const g_argLibraries = g_strLibraries; static string const g_argLink = g_strLink; +static string const g_argMachine = "machine"; static string const g_argMetadata = g_strMetadata; static string const g_argNatspecDev = g_strNatspecDev; static string const g_argNatspecUser = g_strNatspecUser; @@ -154,6 +161,14 @@ static set<string> const g_combinedJsonArgs{ g_strSrcMapRuntime }; +/// Possible arguments to for --machine +static set<string> const g_machineArgs +{ + g_strEVM, + g_strEVM15, + g_streWasm +}; + static void version() { cout << @@ -539,7 +554,16 @@ Allowed options)", ) ( g_argAssemble.c_str(), - "Switch to assembly mode, ignoring all options and assumes input is assembly." + "Switch to assembly mode, ignoring all options except --machine and assumes input is assembly." + ) + ( + g_argJulia.c_str(), + "Switch to JULIA mode, ignoring all options except --machine and assumes input is JULIA." + ) + ( + g_argMachine.c_str(), + po::value<string>()->value_name(boost::join(g_machineArgs, ",")), + "Target machine in assembly or JULIA mode." ) ( g_argLink.c_str(), @@ -689,11 +713,30 @@ bool CommandLineInterface::processInput() if (!parseLibraryOption(library)) return false; - if (m_args.count(g_argAssemble)) + if (m_args.count(g_argAssemble) || m_args.count(g_argJulia)) { // switch to assembly mode m_onlyAssemble = true; - return assemble(); + using Input = AssemblyStack::Language; + using Machine = AssemblyStack::Machine; + Input inputLanguage = m_args.count(g_argJulia) ? Input::JULIA : Input::Assembly; + Machine targetMachine = Machine::EVM; + if (m_args.count(g_argMachine)) + { + string machine = m_args[g_argMachine].as<string>(); + if (machine == g_strEVM) + targetMachine = Machine::EVM; + else if (machine == g_strEVM15) + targetMachine = Machine::EVM15; + else if (machine == g_streWasm) + targetMachine = Machine::eWasm; + else + { + cerr << "Invalid option for --machine: " << machine << endl; + return false; + } + } + return assemble(inputLanguage, targetMachine); } if (m_args.count(g_argLink)) { @@ -916,10 +959,9 @@ void CommandLineInterface::handleAst(string const& _argStr) bool CommandLineInterface::actOnInput() { - if (m_args.count(g_argStandardJSON)) + if (m_args.count(g_argStandardJSON) || m_onlyAssemble) + // Already done in "processInput" phase. return true; - else if (m_onlyAssemble) - outputAssembly(); else if (m_onlyLink) writeLinkedFiles(); else @@ -980,21 +1022,20 @@ void CommandLineInterface::writeLinkedFiles() writeFile(src.first, src.second); } -bool CommandLineInterface::assemble() +bool CommandLineInterface::assemble( + AssemblyStack::Language _language, + AssemblyStack::Machine _targetMachine +) { bool successful = true; - map<string, shared_ptr<Scanner>> scanners; + map<string, AssemblyStack> assemblyStacks; for (auto const& src: m_sourceCodes) { + auto& stack = assemblyStacks[src.first] = AssemblyStack(_language); try { - auto scanner = make_shared<Scanner>(CharStream(src.second), src.first); - scanners[src.first] = scanner; - if (!m_assemblyStacks[src.first].parse(scanner)) + if (!stack.parseAndAnalyze(src.first, src.second)) successful = false; - else - //@TODO we should not just throw away the result here - m_assemblyStacks[src.first].assemble(); } catch (Exception const& _exception) { @@ -1007,31 +1048,49 @@ bool CommandLineInterface::assemble() return false; } } - for (auto const& stack: m_assemblyStacks) + for (auto const& sourceAndStack: assemblyStacks) { - for (auto const& error: stack.second.errors()) + auto const& stack = sourceAndStack.second; + for (auto const& error: stack.errors()) SourceReferenceFormatter::printExceptionInformation( cerr, *error, (error->type() == Error::Type::Warning) ? "Warning" : "Error", - [&](string const& _source) -> Scanner const& { return *scanners.at(_source); } + [&](string const&) -> Scanner const& { return stack.scanner(); } ); - if (!Error::containsOnlyWarnings(stack.second.errors())) + if (!Error::containsOnlyWarnings(stack.errors())) successful = false; } - return successful; -} + if (!successful) + return false; -void CommandLineInterface::outputAssembly() -{ for (auto const& src: m_sourceCodes) { - cout << endl << "======= " << src.first << " =======" << endl; - eth::Assembly assembly = m_assemblyStacks[src.first].assemble(); - cout << assembly.assemble().toHex() << endl; - assembly.stream(cout, "", m_sourceCodes); + string machine = + _targetMachine == AssemblyStack::Machine::EVM ? "EVM" : + _targetMachine == AssemblyStack::Machine::EVM15 ? "EVM 1.5" : + "eWasm"; + cout << endl << "======= " << src.first << " (" << machine << ") =======" << endl; + AssemblyStack& stack = assemblyStacks[src.first]; + try + { + cout << stack.assemble(_targetMachine).toHex() << endl; + } + catch (Exception const& _exception) + { + cerr << "Exception while assembling: " << boost::diagnostic_information(_exception) << endl; + return false; + } + catch (...) + { + cerr << "Unknown exception while assembling." << endl; + return false; + } + cout << stack.print() << endl; } + + return true; } void CommandLineInterface::outputCompilationResults() diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index ebf52625..b482c20b 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -22,7 +22,7 @@ #pragma once #include <libsolidity/interface/CompilerStack.h> -#include <libsolidity/inlineasm/AsmStack.h> +#include <libsolidity/interface/AssemblyStack.h> #include <boost/program_options.hpp> #include <boost/filesystem/path.hpp> @@ -54,9 +54,7 @@ private: bool link(); void writeLinkedFiles(); - /// Parse assembly input. - bool assemble(); - void outputAssembly(); + bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); void outputCompilationResults(); @@ -86,6 +84,7 @@ private: bool m_error = false; ///< If true, some error occurred. bool m_onlyAssemble = false; + bool m_onlyLink = false; /// Compiler arguments variable map @@ -98,8 +97,6 @@ private: std::map<std::string, h160> m_libraries; /// Solidity compiler stack std::unique_ptr<dev::solidity::CompilerStack> m_compiler; - /// Assembly stacks for assembly-only mode - std::map<std::string, assembly::InlineAssemblyStack> m_assemblyStacks; }; } |