From eaa13d42a09155200127418762940ca652b050c5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 23 May 2017 18:57:06 +0200 Subject: Support multiple assembly front and backends. --- .../interface/MultiBackendAssemblyStack.cpp | 85 ++++++++++++++++++++++ libsolidity/interface/MultiBackendAssemblyStack.h | 85 ++++++++++++++++++++++ solc/CommandLineInterface.cpp | 70 +++++++++++------- solc/CommandLineInterface.h | 10 +-- 4 files changed, 217 insertions(+), 33 deletions(-) create mode 100644 libsolidity/interface/MultiBackendAssemblyStack.cpp create mode 100644 libsolidity/interface/MultiBackendAssemblyStack.h diff --git a/libsolidity/interface/MultiBackendAssemblyStack.cpp b/libsolidity/interface/MultiBackendAssemblyStack.cpp new file mode 100644 index 00000000..cf16b627 --- /dev/null +++ b/libsolidity/interface/MultiBackendAssemblyStack.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 . +*/ +/** + * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * eWasm as output. + */ + + +#include + +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + + +Scanner const& MultiBackendAssemblyStack::scanner() const +{ + solAssert(m_scanner, ""); + return *m_scanner; +} + +bool MultiBackendAssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source) +{ + m_analysisSuccessful = false; + m_scanner = make_shared(CharStream(_source), _sourceName); + m_parserResult = assembly::Parser(m_errors, m_input == Input::JULIA).parse(m_scanner); + if (!m_errors.empty()) + return false; + solAssert(m_parserResult, ""); + + m_analysisInfo = make_shared(); + assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errors); + m_analysisSuccessful = analyzer.analyze(*m_parserResult); + return m_analysisSuccessful; +} + +eth::LinkerObject MultiBackendAssemblyStack::assemble() +{ + solAssert(m_analysisSuccessful, ""); + solAssert(m_parserResult, ""); + solAssert(m_analysisInfo, ""); + + switch (m_targetMachine) + { + 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 MultiBackendAssemblyStack::print() +{ + solAssert(m_parserResult, ""); + return assembly::AsmPrinter(m_input == Input::JULIA)(*m_parserResult); +} diff --git a/libsolidity/interface/MultiBackendAssemblyStack.h b/libsolidity/interface/MultiBackendAssemblyStack.h new file mode 100644 index 00000000..fed9e8f9 --- /dev/null +++ b/libsolidity/interface/MultiBackendAssemblyStack.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 . +*/ +/** + * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * eWasm as output. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +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 MultiBackendAssemblyStack +{ +public: + enum class Input { JULIA, Assembly }; + enum class Machine { EVM, EVM15, eWasm }; + + MultiBackendAssemblyStack(Input _input = Input::Assembly, Machine _targetMachine = Machine::EVM): + m_input(_input), + m_targetMachine(_targetMachine) + {} + + /// @returns the scanner used during parsing + Scanner const& scanner() const; + + /// Runs parsing and analysis steps, returns false if input cannot be assembled. + bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source); + + /// Run the assembly step (should only be called after parseAndAnalyze). + eth::LinkerObject assemble(); + + ErrorList const& errors() const { return m_errors; } + + /// Pretty-print the input after having parsed it. + std::string print(); + +private: + + Input m_input = Input::Assembly; + Machine m_targetMachine = Machine::EVM; + + std::shared_ptr m_scanner; + + bool m_analysisSuccessful = false; + std::shared_ptr m_parserResult; + std::shared_ptr m_analysisInfo; + ErrorList m_errors; +}; + +} +} diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 36676119..701cf163 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -35,8 +35,8 @@ #include #include #include +#include #include -#include #include #include @@ -83,7 +83,7 @@ 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_streWasm = "ewasm"; static string const g_strFormal = "formal"; static string const g_strGas = "gas"; static string const g_strHelp = "help"; @@ -166,7 +166,7 @@ static set const g_machineArgs { g_strEVM, g_strEVM15, - g_streWASM + g_streWasm }; static void version() @@ -717,22 +717,26 @@ bool CommandLineInterface::processInput() { // switch to assembly mode m_onlyAssemble = true; + using Input = MultiBackendAssemblyStack::Input; + using Machine = MultiBackendAssemblyStack::Machine; + Input input = 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(); if (machine == g_strEVM) - m_assemblyMachine = AssemblyMachine::EVM; + targetMachine = Machine::EVM; else if (machine == g_strEVM15) - m_assemblyMachine = AssemblyMachine::EVM15; - else if (machine == g_streWASM) - m_assemblyMachine = AssemblyMachine::eWasm; + targetMachine = Machine::EVM15; + else if (machine == g_streWasm) + targetMachine = Machine::eWasm; else { cerr << "Invalid option for --machine: " << machine << endl; return false; } } - return assemble(); + return assemble(input, targetMachine); } if (m_args.count(g_argLink)) { @@ -1018,22 +1022,20 @@ void CommandLineInterface::writeLinkedFiles() writeFile(src.first, src.second); } -bool CommandLineInterface::assemble() +bool CommandLineInterface::assemble( + MultiBackendAssemblyStack::Input _input, + MultiBackendAssemblyStack::Machine _targetMachine +) { bool successful = true; - map> scanners; - map assemblyStacks; + map assemblyStacks; for (auto const& src: m_sourceCodes) { + auto& stack = assemblyStacks[src.first] = MultiBackendAssemblyStack(_input, _targetMachine); try { - auto scanner = make_shared(CharStream(src.second), src.first); - scanners[src.first] = scanner; - if (!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 - assemblyStacks[src.first].assemble(); } catch (Exception const& _exception) { @@ -1046,16 +1048,17 @@ bool CommandLineInterface::assemble() return false; } } - for (auto const& stack: 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; } @@ -1064,10 +1067,27 @@ bool CommandLineInterface::assemble() for (auto const& src: m_sourceCodes) { - cout << endl << "======= " << src.first << " =======" << endl; - eth::Assembly assembly = 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; diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index cf8652f4..8c79e32d 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -22,6 +22,7 @@ #pragma once #include +#include #include #include @@ -53,9 +54,7 @@ private: bool link(); void writeLinkedFiles(); - /// Parse assembly input. - bool assemble(); - void outputAssembly(); + bool assemble(MultiBackendAssemblyStack::Input _input, MultiBackendAssemblyStack::Machine _targetMachine); void outputCompilationResults(); @@ -85,11 +84,6 @@ private: bool m_error = false; ///< If true, some error occurred. bool m_onlyAssemble = false; - /// Settings to use in assembly / JULIA mode. - enum class AssemblyInput { JULIA, Assembly }; - enum class AssemblyMachine { EVM, EVM15, eWasm }; - AssemblyInput m_assemblyInput = AssemblyInput::Assembly; - AssemblyMachine m_assemblyMachine = AssemblyMachine::EVM; bool m_onlyLink = false; -- cgit v1.2.3