diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index db669d148..62e283800 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include @@ -55,6 +57,8 @@ shared_ptr languageToDialect(AssemblyStack::Language _language, EVMVers return EVMDialect::strictAssemblyForEVMObjects(_version); case AssemblyStack::Language::Yul: return Dialect::yul(); + case AssemblyStack::Language::EWasm: + return make_shared(); } solAssert(false, ""); return Dialect::yul(); @@ -178,7 +182,14 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const return object; } case Machine::eWasm: - solUnimplemented("eWasm backend is not yet implemented."); + { + solAssert(m_language == Language::EWasm, ""); + shared_ptr dialect = languageToDialect(m_language, EVMVersion{}); + + MachineAssemblyObject object; + object.assembly = EWasmObjectCompiler::compile(*m_parserResult, *dialect); + return object; + } } // unreachable return MachineAssemblyObject(); diff --git a/libyul/AssemblyStack.h b/libyul/AssemblyStack.h index fb6686c39..40649fc56 100644 --- a/libyul/AssemblyStack.h +++ b/libyul/AssemblyStack.h @@ -57,7 +57,7 @@ struct MachineAssemblyObject class AssemblyStack { public: - enum class Language { Yul, Assembly, StrictAssembly }; + enum class Language { Yul, Assembly, StrictAssembly, EWasm }; enum class Machine { EVM, EVM15, eWasm }; AssemblyStack(): diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 2e04bf9be..b08e839f6 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -36,6 +36,12 @@ add_library(yul backends/evm/EVMDialect.h backends/evm/EVMObjectCompiler.cpp backends/evm/EVMObjectCompiler.h + backends/wasm/EWasmCodeTransform.cpp + backends/wasm/EWasmCodeTransform.h + backends/wasm/EWasmObjectCompiler.cpp + backends/wasm/EWasmObjectCompiler.h + backends/wasm/EWasmToText.cpp + backends/wasm/EWasmToText.h backends/evm/NoOutputAssembly.h backends/evm/NoOutputAssembly.cpp backends/wasm/WasmDialect.cpp diff --git a/libyul/backends/wasm/EWasmAST.h b/libyul/backends/wasm/EWasmAST.h new file mode 100644 index 000000000..f819ae3ab --- /dev/null +++ b/libyul/backends/wasm/EWasmAST.h @@ -0,0 +1,83 @@ +/* + 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 . +*/ +/** + * Simplified in-memory representation of a Wasm AST. + */ + +#pragma once + +#include +#include +#include + +namespace yul +{ +namespace wasm +{ + +struct Literal; +struct LocalVariable; +struct GlobalVariable; +struct Label; +struct FunctionCall; +struct BuiltinCall; +struct LocalAssignment; +struct GlobalAssignment; +struct Block; +struct If; +struct Loop; +struct Break; +struct Continue; +using Expression = boost::variant< + Literal, LocalVariable, GlobalVariable, Label, + FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment, + Block, If, Loop, Break, Continue +>; + +struct Literal { uint64_t value; }; +struct LocalVariable { std::string name; }; +struct GlobalVariable { std::string name; }; +struct Label { std::string name; }; +struct FunctionCall { std::string functionName; std::vector arguments; }; +struct BuiltinCall { std::string functionName; std::vector arguments; }; +struct LocalAssignment { std::string variableName; std::unique_ptr value; }; +struct GlobalAssignment { std::string variableName; std::unique_ptr value; }; +struct Block { std::string labelName; std::vector statements; }; +struct If { + std::unique_ptr condition; + std::vector statements; + std::unique_ptr> elseStatements; +}; +struct Loop { std::string labelName; std::vector statements; }; +struct Break { Label label; }; +struct Continue { Label label; }; + +struct VariableDeclaration { std::string variableName; }; +struct GlobalVariableDeclaration { std::string variableName; }; + +struct FunctionDefinition +{ + std::string name; + std::vector parameterNames; + bool returns; + std::vector locals; + std::vector body; +}; + + +} +} diff --git a/libyul/backends/wasm/EWasmCodeTransform.cpp b/libyul/backends/wasm/EWasmCodeTransform.cpp new file mode 100644 index 000000000..a67bd2c3a --- /dev/null +++ b/libyul/backends/wasm/EWasmCodeTransform.cpp @@ -0,0 +1,318 @@ +/* + 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 . +*/ +/** +* Common code generator for translating Yul / inline assembly to EWasm. +*/ + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; + +string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) +{ + EWasmCodeTransform transform(_dialect, _ast); + vector functions; + + for (auto const& statement: _ast.statements) + { + yulAssert( + statement.type() == typeid(yul::FunctionDefinition), + "Expected only function definitions at the highest level." + ); + functions.emplace_back(transform.translateFunction(boost::get(statement))); + } + + return EWasmToText{}.run(transform.m_globalVariables, functions); +} + +wasm::Expression EWasmCodeTransform::generateMultiAssignment( + vector _variableNames, + unique_ptr _firstValue +) +{ + yulAssert(!_variableNames.empty(), ""); + wasm::LocalAssignment assignment{move(_variableNames.front()), std::move(_firstValue)}; + + if (_variableNames.size() == 1) + return move(assignment); + + wasm::Block block; + block.statements.emplace_back(move(assignment)); + for (size_t i = 1; i < _variableNames.size(); ++i) + block.statements.emplace_back(wasm::LocalAssignment{ + move(_variableNames.at(i)), + make_unique(wasm::GlobalVariable{m_globalVariables.at(i - 1).variableName}) + }); + return move(block); +} + +wasm::Expression EWasmCodeTransform::operator()(VariableDeclaration const& _varDecl) +{ + vector variableNames; + for (auto const& var: _varDecl.variables) + { + variableNames.emplace_back(var.name.str()); + m_localVariables.emplace_back(wasm::VariableDeclaration{variableNames.back()}); + } + + if (_varDecl.value) + return generateMultiAssignment(move(variableNames), visit(*_varDecl.value)); + else + return wasm::BuiltinCall{"nop", {}}; +} + +wasm::Expression EWasmCodeTransform::operator()(Assignment const& _assignment) +{ + vector variableNames; + for (auto const& var: _assignment.variableNames) + variableNames.emplace_back(var.name.str()); + return generateMultiAssignment(move(variableNames), visit(*_assignment.value)); +} + +wasm::Expression EWasmCodeTransform::operator()(StackAssignment const&) +{ + yulAssert(false, ""); + return {}; +} + +wasm::Expression EWasmCodeTransform::operator()(ExpressionStatement const& _statement) +{ + return visitReturnByValue(_statement.expression); +} + +wasm::Expression EWasmCodeTransform::operator()(Label const&) +{ + yulAssert(false, ""); + return {}; +} + +wasm::Expression EWasmCodeTransform::operator()(FunctionalInstruction const&) +{ + yulAssert(false, ""); + return {}; +} + +wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call) +{ + if (m_dialect.builtin(_call.functionName.name)) + return wasm::BuiltinCall{_call.functionName.name.str(), visit(_call.arguments)}; + else + // If this function returns multiple values, then the first one will + // be returned in the expression itself and the others in global variables. + // The values have to be used right away in an assignment or variable declaration, + // so it is handled there. + return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)}; +} + +wasm::Expression EWasmCodeTransform::operator()(Identifier const& _identifier) +{ + return wasm::LocalVariable{_identifier.name.str()}; +} + +wasm::Expression EWasmCodeTransform::operator()(Literal const& _literal) +{ + u256 value = valueOfLiteral(_literal); + yulAssert(value <= numeric_limits::max(), ""); + return wasm::Literal{uint64_t(value)}; +} + +wasm::Expression EWasmCodeTransform::operator()(yul::Instruction const&) +{ + yulAssert(false, "EVM instruction used for Wasm code generation."); + return {}; +} + +wasm::Expression EWasmCodeTransform::operator()(If const& _if) +{ + return wasm::If{visit(*_if.condition), visit(_if.body.statements), {}}; +} + +wasm::Expression EWasmCodeTransform::operator()(Switch const& _switch) +{ + wasm::Block block; + string condition = m_nameDispenser.newName("condition"_yulstring).str(); + m_localVariables.emplace_back(wasm::VariableDeclaration{condition}); + block.statements.emplace_back(wasm::LocalAssignment{condition, visit(*_switch.expression)}); + + vector* currentBlock = &block.statements; + for (size_t i = 0; i < _switch.cases.size(); ++i) + { + Case const& c = _switch.cases.at(i); + if (c.value) + { + wasm::BuiltinCall comparison{"i64.eq", {}}; + comparison.arguments.emplace_back(wasm::LocalVariable{condition}); + comparison.arguments.emplace_back(visitReturnByValue(*c.value)); + wasm::If ifStmnt{ + make_unique(move(comparison)), + visit(c.body.statements), + {} + }; + vector* nextBlock = nullptr; + if (i != _switch.cases.size() - 1) + { + ifStmnt.elseStatements = make_unique>(); + nextBlock = ifStmnt.elseStatements.get(); + } + currentBlock->emplace_back(move(ifStmnt)); + currentBlock = nextBlock; + } + else + { + yulAssert(i == _switch.cases.size() - 1, "Default case must be last."); + *currentBlock += visit(c.body.statements); + } + } + return move(block); +} + +wasm::Expression EWasmCodeTransform::operator()(FunctionDefinition const&) +{ + yulAssert(false, "Should not have visited here."); + return {}; +} + +wasm::Expression EWasmCodeTransform::operator()(ForLoop const& _for) +{ + string breakLabel = newLabel(); + string continueLabel = newLabel(); + m_breakContinueLabelNames.push({breakLabel, continueLabel}); + + // The AST is constructed in this weird way because of some strange + // problem with move semantics. + wasm::BuiltinCall loopCondition{"i64.eqz", {}}; + loopCondition.arguments.emplace_back(visitReturnByValue(*_for.condition)); + + wasm::BuiltinCall conditionCheck{"br_if", {}}; + conditionCheck.arguments.emplace_back(wasm::Label{breakLabel}); + conditionCheck.arguments.emplace_back(move(loopCondition)); + + wasm::Loop loop; + loop.statements = visit(_for.pre.statements); + loop.statements.emplace_back(move(conditionCheck)); + loop.statements.emplace_back(wasm::Block{continueLabel, visit(_for.body.statements)}); + loop.statements += visit(_for.post.statements); + + wasm::Block breakBlock{breakLabel, {}}; + breakBlock.statements.emplace_back(move(loop)); + return move(breakBlock); +} + +wasm::Expression EWasmCodeTransform::operator()(Break const&) +{ + return wasm::Break{wasm::Label{m_breakContinueLabelNames.top().first}}; +} + +wasm::Expression EWasmCodeTransform::operator()(Continue const&) +{ + return wasm::Continue{wasm::Label{m_breakContinueLabelNames.top().second}}; +} + +wasm::Expression EWasmCodeTransform::operator()(Block const& _block) +{ + return wasm::Block{{}, visit(_block.statements)}; +} + +unique_ptr EWasmCodeTransform::visit(yul::Expression const& _expression) +{ + return make_unique(boost::apply_visitor(*this, _expression)); +} + +wasm::Expression EWasmCodeTransform::visitReturnByValue(yul::Expression const& _expression) +{ + return boost::apply_visitor(*this, _expression); +} + +vector EWasmCodeTransform::visit(vector const& _expressions) +{ + vector ret; + for (auto const& e: _expressions) + ret.emplace_back(visitReturnByValue(e)); + return ret; +} + +wasm::Expression EWasmCodeTransform::visit(yul::Statement const& _statement) +{ + return boost::apply_visitor(*this, _statement); +} + +vector EWasmCodeTransform::visit(vector const& _statements) +{ + vector ret; + for (auto const& s: _statements) + ret.emplace_back(visit(s)); + return ret; +} + +wasm::FunctionDefinition EWasmCodeTransform::translateFunction(yul::FunctionDefinition const& _fun) +{ + wasm::FunctionDefinition fun; + fun.name = _fun.name.str(); + for (auto const& param: _fun.parameters) + fun.parameterNames.emplace_back(param.name.str()); + for (auto const& retParam: _fun.returnVariables) + fun.locals.emplace_back(wasm::VariableDeclaration{retParam.name.str()}); + fun.returns = !_fun.returnVariables.empty(); + + yulAssert(m_localVariables.empty(), ""); + fun.body = visit(_fun.body.statements); + fun.locals += m_localVariables; + + m_localVariables.clear(); + + if (!_fun.returnVariables.empty()) + { + // First return variable is returned directly, the others are stored + // in globals. + allocateGlobals(_fun.returnVariables.size() - 1); + for (size_t i = 1; i < _fun.returnVariables.size(); ++i) + fun.body.emplace_back(wasm::GlobalAssignment{ + m_globalVariables.at(i - 1).variableName, + make_unique(wasm::LocalVariable{_fun.returnVariables.at(i).name.str()}) + }); + fun.body.emplace_back(wasm::LocalVariable{_fun.returnVariables.front().name.str()}); + } + return fun; +} + +string EWasmCodeTransform::newLabel() +{ + return m_nameDispenser.newName("label_"_yulstring).str(); +} + +void EWasmCodeTransform::allocateGlobals(size_t _amount) +{ + while (m_globalVariables.size() < _amount) + m_globalVariables.emplace_back(wasm::GlobalVariableDeclaration{ + m_nameDispenser.newName("global_"_yulstring).str() + }); +} diff --git a/libyul/backends/wasm/EWasmCodeTransform.h b/libyul/backends/wasm/EWasmCodeTransform.h new file mode 100644 index 000000000..399428a31 --- /dev/null +++ b/libyul/backends/wasm/EWasmCodeTransform.h @@ -0,0 +1,96 @@ +/* + 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 . +*/ +/** + * Common code generator for translating Yul / inline assembly to EWasm. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace yul +{ +struct AsmAnalysisInfo; + +class EWasmCodeTransform: public boost::static_visitor +{ +public: + static std::string run(Dialect const& _dialect, yul::Block const& _ast); + +public: + wasm::Expression operator()(yul::Instruction const& _instruction); + wasm::Expression operator()(yul::Literal const& _literal); + wasm::Expression operator()(yul::Identifier const& _identifier); + wasm::Expression operator()(yul::FunctionalInstruction const& _instr); + wasm::Expression operator()(yul::FunctionCall const&); + wasm::Expression operator()(yul::ExpressionStatement const& _statement); + wasm::Expression operator()(yul::Label const& _label); + wasm::Expression operator()(yul::StackAssignment const& _assignment); + wasm::Expression operator()(yul::Assignment const& _assignment); + wasm::Expression operator()(yul::VariableDeclaration const& _varDecl); + wasm::Expression operator()(yul::If const& _if); + wasm::Expression operator()(yul::Switch const& _switch); + wasm::Expression operator()(yul::FunctionDefinition const&); + wasm::Expression operator()(yul::ForLoop const&); + wasm::Expression operator()(yul::Break const&); + wasm::Expression operator()(yul::Continue const&); + wasm::Expression operator()(yul::Block const& _block); + +private: + EWasmCodeTransform( + Dialect const& _dialect, + Block const& _ast + ): + m_dialect(_dialect), + m_nameDispenser(_dialect, _ast) + {} + + std::unique_ptr visit(yul::Expression const& _expression); + wasm::Expression visitReturnByValue(yul::Expression const& _expression); + std::vector visit(std::vector const& _expressions); + wasm::Expression visit(yul::Statement const& _statement); + std::vector visit(std::vector const& _statements); + + /// Returns an assignment or a block containing multiple assignments. + /// @param _variableNames the names of the variables to assign to + /// @param _firstValue the value to be assigned to the first variable. If there + /// is more than one variable, the values are taken from m_globalVariables. + wasm::Expression generateMultiAssignment( + std::vector _variableNames, + std::unique_ptr _firstValue + ); + + wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef); + + std::string newLabel(); + /// Makes sure that there are at least @a _amount global variables. + void allocateGlobals(size_t _amount); + + Dialect const& m_dialect; + NameDispenser m_nameDispenser; + + std::vector m_localVariables; + std::vector m_globalVariables; + std::stack> m_breakContinueLabelNames; +}; + +} diff --git a/libyul/backends/wasm/EWasmObjectCompiler.cpp b/libyul/backends/wasm/EWasmObjectCompiler.cpp new file mode 100644 index 000000000..fb66c9d49 --- /dev/null +++ b/libyul/backends/wasm/EWasmObjectCompiler.cpp @@ -0,0 +1,52 @@ +/* + 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 . +*/ +/** + * Compiler that transforms Yul Objects to EWasm text representation. + */ + +#include + +#include + +#include +#include + +using namespace yul; +using namespace std; + +string EWasmObjectCompiler::compile(Object& _object, Dialect& _dialect) +{ + EWasmObjectCompiler compiler(_dialect); + return compiler.run(_object); +} + +string EWasmObjectCompiler::run(Object& _object) +{ + string ret; + + for (auto& subNode: _object.subObjects) + if (Object* subObject = dynamic_cast(subNode.get())) + ret += compile(*subObject, m_dialect); + else + yulAssert(false, "Data is not yet supported for EWasm."); + + yulAssert(_object.analysisInfo, "No analysis info."); + yulAssert(_object.code, "No code."); + ret += EWasmCodeTransform::run(m_dialect, *_object.code); + + return ret; +} diff --git a/libyul/backends/wasm/EWasmObjectCompiler.h b/libyul/backends/wasm/EWasmObjectCompiler.h new file mode 100644 index 000000000..79ad94bdc --- /dev/null +++ b/libyul/backends/wasm/EWasmObjectCompiler.h @@ -0,0 +1,44 @@ +/* + 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 . +*/ +/** + * Compiler that transforms Yul Objects to EWasm text representation. + */ + +#pragma once + +#include + +namespace yul +{ +struct Object; +struct Dialect; + +class EWasmObjectCompiler +{ +public: + static std::string compile(Object& _object, Dialect& _dialect); +private: + EWasmObjectCompiler(Dialect& _dialect): + m_dialect(_dialect) + {} + + std::string run(Object& _object); + + Dialect& m_dialect; +}; + +} diff --git a/libyul/backends/wasm/EWasmToText.cpp b/libyul/backends/wasm/EWasmToText.cpp new file mode 100644 index 000000000..a0d552d11 --- /dev/null +++ b/libyul/backends/wasm/EWasmToText.cpp @@ -0,0 +1,160 @@ +/* + 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 . +*/ +/** + * Component that transforms interval EWasm representation to text. + */ + +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace yul; + +string EWasmToText::run( + vector const& _globals, + vector const& _functions +) +{ + string ret = "(module\n\n"; + for (auto const& g: _globals) + ret += " (global $" + g.variableName + " (mut i64) (i64.const 0))\n"; + for (auto const& f: _functions) + ret += transform(f) + "\n"; + return move(ret) + ")\n"; +} + +string EWasmToText::operator()(wasm::Literal const& _literal) +{ + return "(i64.const " + to_string(_literal.value) + ")"; +} + +string EWasmToText::operator()(wasm::LocalVariable const& _identifier) +{ + return "(get_local $" + _identifier.name + ")"; +} + +string EWasmToText::operator()(wasm::GlobalVariable const& _identifier) +{ + return "(get_global $" + _identifier.name + ")"; +} + +string EWasmToText::operator()(wasm::Label const& _label) +{ + return "$" + _label.name; +} + +string EWasmToText::operator()(wasm::BuiltinCall const& _builtinCall) +{ + return "(" + _builtinCall.functionName + " " + joinTransformed(_builtinCall.arguments) + ")"; +} + +string EWasmToText::operator()(wasm::FunctionCall const& _functionCall) +{ + return "(call $" + _functionCall.functionName + " " + joinTransformed(_functionCall.arguments) + ")"; +} + +string EWasmToText::operator()(wasm::LocalAssignment const& _assignment) +{ + return "(set_local $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n"; +} + +string EWasmToText::operator()(wasm::GlobalAssignment const& _assignment) +{ + return "(set_global $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n"; +} + +string EWasmToText::operator()(wasm::If const& _if) +{ + string text = "(if " + visit(*_if.condition) + " (then\n" + indented(joinTransformed(_if.statements)) + ")"; + if (_if.elseStatements) + text += "(else\n" + indented(joinTransformed(*_if.elseStatements)) + ")"; + return std::move(text) + ")\n"; +} + +string EWasmToText::operator()(wasm::Loop const& _loop) +{ + string label = _loop.labelName.empty() ? "" : " $" + _loop.labelName; + return "(loop" + move(label) + "\n" + indented(joinTransformed(_loop.statements)) + ")\n"; +} + +string EWasmToText::operator()(wasm::Break const& _break) +{ + return "(break $" + _break.label.name + ")\n"; +} + +string EWasmToText::operator()(wasm::Continue const& _continue) +{ + return "(continue $" + _continue.label.name + ")\n"; +} + +string EWasmToText::operator()(wasm::Block const& _block) +{ + string label = _block.labelName.empty() ? "" : " $" + _block.labelName; + return "(block" + move(label) + "\n" + indented(joinTransformed(_block.statements)) + "\n)\n"; +} + +string EWasmToText::indented(string const& _in) +{ + string replacement; + if (!_in.empty()) + { + replacement = " " + boost::replace_all_copy(_in, "\n", "\n "); + if (_in.back() == '\n') + replacement = replacement.substr(0, replacement.size() - 4); + } + return replacement; +} + +string EWasmToText::transform(wasm::FunctionDefinition const& _function) +{ + string ret = "(func $" + _function.name + "\n"; + for (auto const& param: _function.parameterNames) + ret += " (param $" + param + " i64)\n"; + if (_function.returns) + ret += " (result i64)\n"; + for (auto const& local: _function.locals) + ret += " (local $" + local.variableName + " i64)\n"; + ret += indented(joinTransformed(_function.body)); + if (ret.back() != '\n') + ret += '\n'; + ret += ")\n"; + return ret; +} + + +string EWasmToText::visit(wasm::Expression const& _expression) +{ + return boost::apply_visitor(*this, _expression); +} + +string EWasmToText::joinTransformed(vector const& _expressions) +{ + string ret; + for (auto const& e: _expressions) + { + string t = visit(e); + if (!t.empty() && !ret.empty() && ret.back() != '\n') + ret += ' '; + ret += move(t); + } + return ret; +} diff --git a/libyul/backends/wasm/EWasmToText.h b/libyul/backends/wasm/EWasmToText.h new file mode 100644 index 000000000..61bcf5b32 --- /dev/null +++ b/libyul/backends/wasm/EWasmToText.h @@ -0,0 +1,63 @@ +/* + 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 . +*/ +/** + * Component that transforms interval EWasm representation to text. + */ + +#pragma once + +#include + +#include + +namespace yul +{ +struct AsmAnalysisInfo; + +class EWasmToText: public boost::static_visitor +{ +public: + std::string run( + std::vector const& _globals, + std::vector const& _functions + ); + +public: + std::string operator()(wasm::Literal const& _literal); + std::string operator()(wasm::LocalVariable const& _identifier); + std::string operator()(wasm::GlobalVariable const& _identifier); + std::string operator()(wasm::Label const& _label); + std::string operator()(wasm::BuiltinCall const& _builinCall); + std::string operator()(wasm::FunctionCall const& _functionCall); + std::string operator()(wasm::LocalAssignment const& _assignment); + std::string operator()(wasm::GlobalAssignment const& _assignment); + std::string operator()(wasm::If const& _if); + std::string operator()(wasm::Loop const& _loop); + std::string operator()(wasm::Break const& _break); + std::string operator()(wasm::Continue const& _continue); + std::string operator()(wasm::Block const& _block); + +private: + std::string indented(std::string const& _in); + + std::string transform(wasm::FunctionDefinition const& _function); + + std::string visit(wasm::Expression const& _expression); + std::string joinTransformed(std::vector const& _expressions); +}; + +} diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index 5e03b037f..62195c96b 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -23,8 +23,8 @@ using namespace std; using namespace yul; -WasmDialect::WasmDialect(AsmFlavour _flavour): - Dialect{_flavour} +WasmDialect::WasmDialect(): + Dialect{AsmFlavour::Strict} { for (auto const& name: { "i64.add", @@ -65,7 +65,7 @@ BuiltinFunction const* WasmDialect::builtin(YulString _name) const void WasmDialect::addFunction(string _name, size_t _params, size_t _returns) { - YulString name{std::move(_name)}; + YulString name{move(_name)}; BuiltinFunction& f = m_functions[name]; f.name = name; f.parameters.resize(_params); diff --git a/libyul/backends/wasm/WasmDialect.h b/libyul/backends/wasm/WasmDialect.h index 71509ca08..119635289 100644 --- a/libyul/backends/wasm/WasmDialect.h +++ b/libyul/backends/wasm/WasmDialect.h @@ -42,7 +42,7 @@ struct Object; */ struct WasmDialect: public Dialect { - WasmDialect(AsmFlavour _flavour); + WasmDialect(); BuiltinFunction const* builtin(YulString _name) const override; diff --git a/scripts/codespell_whitelist.txt b/scripts/codespell_whitelist.txt index 0f5013cb1..b9467a805 100644 --- a/scripts/codespell_whitelist.txt +++ b/scripts/codespell_whitelist.txt @@ -2,3 +2,4 @@ iff nd assignend uint +mut diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index d2b9e4ac4..764376236 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -882,6 +882,8 @@ bool CommandLineInterface::processInput() return false; } } + if (targetMachine == Machine::eWasm && inputLanguage == Input::StrictAssembly) + inputLanguage = Input::EWasm; if (optimize && inputLanguage != Input::StrictAssembly) { serr() <<