From ec27f484a1e9b7075c79d836333d70b112243f4b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 18 Apr 2019 14:39:48 +0200 Subject: [PATCH 1/5] Prototype for Wasm code transform into in-memory representation. --- libyul/AssemblyStack.cpp | 13 +- libyul/AssemblyStack.h | 2 +- libyul/CMakeLists.txt | 4 + libyul/backends/wasm/EWasmAST.h | 69 ++++++ libyul/backends/wasm/EWasmCodeTransform.cpp | 242 +++++++++++++++++++ libyul/backends/wasm/EWasmCodeTransform.h | 82 +++++++ libyul/backends/wasm/EWasmObjectCompiler.cpp | 52 ++++ libyul/backends/wasm/EWasmObjectCompiler.h | 44 ++++ libyul/backends/wasm/WasmDialect.cpp | 6 +- libyul/backends/wasm/WasmDialect.h | 2 +- 10 files changed, 510 insertions(+), 6 deletions(-) create mode 100644 libyul/backends/wasm/EWasmAST.h create mode 100644 libyul/backends/wasm/EWasmCodeTransform.cpp create mode 100644 libyul/backends/wasm/EWasmCodeTransform.h create mode 100644 libyul/backends/wasm/EWasmObjectCompiler.cpp create mode 100644 libyul/backends/wasm/EWasmObjectCompiler.h 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 b70d813b4..90534eac7 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -36,6 +36,10 @@ 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/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..16cc2024e --- /dev/null +++ b/libyul/backends/wasm/EWasmAST.h @@ -0,0 +1,69 @@ +/* + 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 Identifier; +struct Label; +struct FunctionCall; +struct BuiltinCall; +struct LocalAssignment; +struct Block; +struct If; +struct Loop; +struct Break; +struct Continue; +using Expression = boost::variant; + +struct Literal { uint64_t value; }; +struct Identifier { 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 Block { std::string labelName; std::vector statements; }; +struct If { std::unique_ptr condition; std::vector statements; }; +struct Loop { std::string labelName; std::vector statements; }; +struct Break { Label label; }; +struct Continue { Label label; }; + +struct VariableDeclaration { 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..162bbb204 --- /dev/null +++ b/libyul/backends/wasm/EWasmCodeTransform.cpp @@ -0,0 +1,242 @@ +/* + 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 + +using namespace std; +using namespace dev; +using namespace yul; + +string EWasmCodeTransform::run(yul::Block const& _ast) +{ + vector functions; + + for (auto const& statement: _ast.statements) + { + yulAssert(statement.type() == typeid(yul::FunctionDefinition), ""); + functions.emplace_back(translateFunction(boost::get(statement))); + } + + // TODO translate to text representation + + return {}; +} + +wasm::Expression EWasmCodeTransform::operator()(VariableDeclaration const& _varDecl) +{ + for (auto const& var: _varDecl.variables) + m_localVariables.emplace_back(wasm::VariableDeclaration{var.name.str()}); + + if (_varDecl.value) + { + // TODO otherwise, we have to work with globals. + solUnimplementedAssert(_varDecl.variables.size() == 1, "Only single-variable assignments supported."); + return wasm::LocalAssignment{ + _varDecl.variables.front().name.str(), + visit(*_varDecl.value) + }; + } + else + // TODO this could be handled better. + return wasm::BuiltinCall{"nop", {}}; +} + +wasm::Expression EWasmCodeTransform::operator()(Assignment const& _assignment) +{ + solUnimplementedAssert(_assignment.variableNames.size() == 1, "Only single-variable assignments supported."); + return wasm::LocalAssignment{ + _assignment.variableNames.front().name.str(), + 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 + return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)}; +} + +wasm::Expression EWasmCodeTransform::operator()(Identifier const& _identifier) +{ + return wasm::Identifier{_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, ""); + return {}; +} + +wasm::Expression EWasmCodeTransform::operator()(If const& _if) +{ + return wasm::If{visit(*_if.condition), visit(_if.body.statements)}; +} + +wasm::Expression EWasmCodeTransform::operator()(Switch const&) +{ + solUnimplementedAssert(false, ""); + return {}; +} + +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(); + yulAssert(_fun.returnVariables.size() <= 1, ""); + if (_fun.returnVariables.size() == 1) + fun.body.emplace_back(wasm::Identifier{_fun.returnVariables.front().name.str()}); + return fun; +} + +string EWasmCodeTransform::newLabel() +{ + // TODO this should not clash with other identifiers! + return "label_" + to_string(++m_labelCounter); +} diff --git a/libyul/backends/wasm/EWasmCodeTransform.h b/libyul/backends/wasm/EWasmCodeTransform.h new file mode 100644 index 000000000..9876dd54c --- /dev/null +++ b/libyul/backends/wasm/EWasmCodeTransform.h @@ -0,0 +1,82 @@ +/* + 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 + +namespace yul +{ +struct AsmAnalysisInfo; + +class EWasmCodeTransform: public boost::static_visitor +{ +public: + EWasmCodeTransform( + AsmAnalysisInfo&, + Dialect const& _dialect + ): + m_dialect(_dialect) + {} + + std::string run(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: + 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); + + wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef); + + std::string newLabel(); + + std::vector m_localVariables; + size_t m_labelCounter = 0; + std::stack> m_breakContinueLabelNames; + + Dialect const& m_dialect; +}; + +} diff --git a/libyul/backends/wasm/EWasmObjectCompiler.cpp b/libyul/backends/wasm/EWasmObjectCompiler.cpp new file mode 100644 index 000000000..a2e2dad50 --- /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{*_object.analysisInfo, m_dialect}.run(*_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/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; From f6c6871bce382c9cf56660cac1870241f53671c6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 18 Apr 2019 16:37:59 +0200 Subject: [PATCH 2/5] EWasm to text transformation. --- libyul/CMakeLists.txt | 2 + libyul/backends/wasm/EWasmCodeTransform.cpp | 6 +- libyul/backends/wasm/EWasmToText.cpp | 142 ++++++++++++++++++++ libyul/backends/wasm/EWasmToText.h | 58 ++++++++ 4 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 libyul/backends/wasm/EWasmToText.cpp create mode 100644 libyul/backends/wasm/EWasmToText.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 90534eac7..7052e6ab7 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -40,6 +40,8 @@ add_library(yul 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/EWasmCodeTransform.cpp b/libyul/backends/wasm/EWasmCodeTransform.cpp index 162bbb204..1ca102a3f 100644 --- a/libyul/backends/wasm/EWasmCodeTransform.cpp +++ b/libyul/backends/wasm/EWasmCodeTransform.cpp @@ -20,6 +20,8 @@ #include +#include + #include #include #include @@ -43,9 +45,7 @@ string EWasmCodeTransform::run(yul::Block const& _ast) functions.emplace_back(translateFunction(boost::get(statement))); } - // TODO translate to text representation - - return {}; + return EWasmToText{}.run(functions); } wasm::Expression EWasmCodeTransform::operator()(VariableDeclaration const& _varDecl) diff --git a/libyul/backends/wasm/EWasmToText.cpp b/libyul/backends/wasm/EWasmToText.cpp new file mode 100644 index 000000000..0ae94c6a4 --- /dev/null +++ b/libyul/backends/wasm/EWasmToText.cpp @@ -0,0 +1,142 @@ +/* + 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& _functions) +{ + string ret = "(module\n\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::Identifier const& _identifier) +{ + return "(get.local $" + _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::If const& _if) +{ + return "(if " + visit(*_if.condition) + " (then\n" + indented(joinTransformed(_if.statements)) + "\n))\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..1a752c483 --- /dev/null +++ b/libyul/backends/wasm/EWasmToText.h @@ -0,0 +1,58 @@ +/* + 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& _functions); + +public: + std::string operator()(wasm::Literal const& _literal); + std::string operator()(wasm::Identifier 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::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); +}; + +} From 61e36cbfaab15dabf775f5e902604a3e6ffc27c8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 18 Apr 2019 17:21:40 +0200 Subject: [PATCH 3/5] Activate ewasm dialect in commandline interface. --- libyul/backends/wasm/EWasmToText.cpp | 2 +- solc/CommandLineInterface.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libyul/backends/wasm/EWasmToText.cpp b/libyul/backends/wasm/EWasmToText.cpp index 0ae94c6a4..f9f0eeede 100644 --- a/libyul/backends/wasm/EWasmToText.cpp +++ b/libyul/backends/wasm/EWasmToText.cpp @@ -44,7 +44,7 @@ string EWasmToText::operator()(wasm::Literal const& _literal) string EWasmToText::operator()(wasm::Identifier const& _identifier) { - return "(get.local $" + _identifier.name + ")"; + return "(get_local $" + _identifier.name + ")"; } string EWasmToText::operator()(wasm::Label const& _label) 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() << From c3705f268c589d651a17ea7dc79c56563402aaf7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 23 Apr 2019 10:45:16 +0200 Subject: [PATCH 4/5] Add global variables and support multi-return and multi-assignment. --- libyul/backends/wasm/EWasmAST.h | 16 +++- libyul/backends/wasm/EWasmCodeTransform.cpp | 90 ++++++++++++++------ libyul/backends/wasm/EWasmCodeTransform.h | 38 ++++++--- libyul/backends/wasm/EWasmObjectCompiler.cpp | 2 +- libyul/backends/wasm/EWasmToText.cpp | 19 ++++- libyul/backends/wasm/EWasmToText.h | 9 +- scripts/codespell_whitelist.txt | 1 + 7 files changed, 130 insertions(+), 45 deletions(-) diff --git a/libyul/backends/wasm/EWasmAST.h b/libyul/backends/wasm/EWasmAST.h index 16cc2024e..83817bbc6 100644 --- a/libyul/backends/wasm/EWasmAST.h +++ b/libyul/backends/wasm/EWasmAST.h @@ -30,24 +30,32 @@ namespace wasm { struct Literal; -struct Identifier; +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; +using Expression = boost::variant< + Literal, LocalVariable, GlobalVariable, Label, + FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment, + Block, If, Loop, Break, Continue +>; struct Literal { uint64_t value; }; -struct Identifier { std::string name; }; +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; }; struct Loop { std::string labelName; std::vector statements; }; @@ -55,6 +63,8 @@ struct Break { Label label; }; struct Continue { Label label; }; struct VariableDeclaration { std::string variableName; }; +struct GlobalVariableDeclaration { std::string variableName; }; + struct FunctionDefinition { std::string name; diff --git a/libyul/backends/wasm/EWasmCodeTransform.cpp b/libyul/backends/wasm/EWasmCodeTransform.cpp index 1ca102a3f..835395226 100644 --- a/libyul/backends/wasm/EWasmCodeTransform.cpp +++ b/libyul/backends/wasm/EWasmCodeTransform.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -30,50 +31,68 @@ #include #include +#include using namespace std; using namespace dev; using namespace yul; -string EWasmCodeTransform::run(yul::Block const& _ast) +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), ""); - functions.emplace_back(translateFunction(boost::get(statement))); + functions.emplace_back(transform.translateFunction(boost::get(statement))); } - return EWasmToText{}.run(functions); + return EWasmToText{}.run(transform.m_globalVariables, functions); +} + +wasm::Expression EWasmCodeTransform::generateMultiAssignment( + vector _variableNames, + unique_ptr _firstValue +) +{ + yulAssert(!_variableNames.empty(), ""); + wasm::LocalAssignment assignment{std::move(_variableNames.front()), std::move(_firstValue)}; + + if (_variableNames.size() == 1) + return move(assignment); + + wasm::Block block; + block.statements.emplace_back(std::move(assignment)); + for (size_t i = 1; i < _variableNames.size(); ++i) + block.statements.emplace_back(wasm::LocalAssignment{ + std::move(_variableNames.at(i)), + make_unique(wasm::GlobalVariable{m_globalVariables.at(i - 1).variableName}) + }); + return std::move(block); } wasm::Expression EWasmCodeTransform::operator()(VariableDeclaration const& _varDecl) { + vector variableNames; for (auto const& var: _varDecl.variables) - m_localVariables.emplace_back(wasm::VariableDeclaration{var.name.str()}); + { + variableNames.emplace_back(var.name.str()); + m_localVariables.emplace_back(wasm::VariableDeclaration{variableNames.back()}); + } if (_varDecl.value) - { - // TODO otherwise, we have to work with globals. - solUnimplementedAssert(_varDecl.variables.size() == 1, "Only single-variable assignments supported."); - return wasm::LocalAssignment{ - _varDecl.variables.front().name.str(), - visit(*_varDecl.value) - }; - } + return generateMultiAssignment(std::move(variableNames), visit(*_varDecl.value)); else - // TODO this could be handled better. return wasm::BuiltinCall{"nop", {}}; } wasm::Expression EWasmCodeTransform::operator()(Assignment const& _assignment) { - solUnimplementedAssert(_assignment.variableNames.size() == 1, "Only single-variable assignments supported."); - return wasm::LocalAssignment{ - _assignment.variableNames.front().name.str(), - visit(*_assignment.value) - }; + vector variableNames; + for (auto const& var: _assignment.variableNames) + variableNames.emplace_back(var.name.str()); + return generateMultiAssignment(std::move(variableNames), visit(*_assignment.value)); } wasm::Expression EWasmCodeTransform::operator()(StackAssignment const&) @@ -104,12 +123,16 @@ 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::Identifier{_identifier.name.str()}; + return wasm::LocalVariable{_identifier.name.str()}; } wasm::Expression EWasmCodeTransform::operator()(Literal const& _literal) @@ -121,7 +144,7 @@ wasm::Expression EWasmCodeTransform::operator()(Literal const& _literal) wasm::Expression EWasmCodeTransform::operator()(yul::Instruction const&) { - yulAssert(false, ""); + yulAssert(false, "EVM instruction used for Wasm code generation."); return {}; } @@ -229,14 +252,31 @@ wasm::FunctionDefinition EWasmCodeTransform::translateFunction(yul::FunctionDefi fun.locals += m_localVariables; m_localVariables.clear(); - yulAssert(_fun.returnVariables.size() <= 1, ""); - if (_fun.returnVariables.size() == 1) - fun.body.emplace_back(wasm::Identifier{_fun.returnVariables.front().name.str()}); + + 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() { - // TODO this should not clash with other identifiers! - return "label_" + to_string(++m_labelCounter); + 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 index 9876dd54c..399428a31 100644 --- a/libyul/backends/wasm/EWasmCodeTransform.h +++ b/libyul/backends/wasm/EWasmCodeTransform.h @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -33,14 +34,7 @@ struct AsmAnalysisInfo; class EWasmCodeTransform: public boost::static_visitor { public: - EWasmCodeTransform( - AsmAnalysisInfo&, - Dialect const& _dialect - ): - m_dialect(_dialect) - {} - - std::string run(yul::Block const& _ast); + static std::string run(Dialect const& _dialect, yul::Block const& _ast); public: wasm::Expression operator()(yul::Instruction const& _instruction); @@ -62,21 +56,41 @@ public: 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(); - - std::vector m_localVariables; - size_t m_labelCounter = 0; - std::stack> m_breakContinueLabelNames; + /// 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 index a2e2dad50..fb66c9d49 100644 --- a/libyul/backends/wasm/EWasmObjectCompiler.cpp +++ b/libyul/backends/wasm/EWasmObjectCompiler.cpp @@ -46,7 +46,7 @@ string EWasmObjectCompiler::run(Object& _object) yulAssert(_object.analysisInfo, "No analysis info."); yulAssert(_object.code, "No code."); - ret += EWasmCodeTransform{*_object.analysisInfo, m_dialect}.run(*_object.code); + ret += EWasmCodeTransform::run(m_dialect, *_object.code); return ret; } diff --git a/libyul/backends/wasm/EWasmToText.cpp b/libyul/backends/wasm/EWasmToText.cpp index f9f0eeede..633f90dcb 100644 --- a/libyul/backends/wasm/EWasmToText.cpp +++ b/libyul/backends/wasm/EWasmToText.cpp @@ -29,9 +29,14 @@ using namespace std; using namespace yul; -string EWasmToText::run(vector const& _functions) +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"; @@ -42,11 +47,16 @@ string EWasmToText::operator()(wasm::Literal const& _literal) return "(i64.const " + to_string(_literal.value) + ")"; } -string EWasmToText::operator()(wasm::Identifier const& _identifier) +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; @@ -67,6 +77,11 @@ 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) { return "(if " + visit(*_if.condition) + " (then\n" + indented(joinTransformed(_if.statements)) + "\n))\n"; diff --git a/libyul/backends/wasm/EWasmToText.h b/libyul/backends/wasm/EWasmToText.h index 1a752c483..61bcf5b32 100644 --- a/libyul/backends/wasm/EWasmToText.h +++ b/libyul/backends/wasm/EWasmToText.h @@ -31,15 +31,20 @@ struct AsmAnalysisInfo; class EWasmToText: public boost::static_visitor { public: - std::string run(std::vector const& _functions); + std::string run( + std::vector const& _globals, + std::vector const& _functions + ); public: std::string operator()(wasm::Literal const& _literal); - std::string operator()(wasm::Identifier const& _identifier); + 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); 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 From 01d1d08fea2f61ae9ff0762ff8af2b9ada84a2b3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 23 Apr 2019 21:32:58 +0200 Subject: [PATCH 5/5] Implement switch. --- libyul/backends/wasm/EWasmAST.h | 6 ++- libyul/backends/wasm/EWasmCodeTransform.cpp | 58 +++++++++++++++++---- libyul/backends/wasm/EWasmToText.cpp | 5 +- 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/libyul/backends/wasm/EWasmAST.h b/libyul/backends/wasm/EWasmAST.h index 83817bbc6..f819ae3ab 100644 --- a/libyul/backends/wasm/EWasmAST.h +++ b/libyul/backends/wasm/EWasmAST.h @@ -57,7 +57,11 @@ 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; }; +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; }; diff --git a/libyul/backends/wasm/EWasmCodeTransform.cpp b/libyul/backends/wasm/EWasmCodeTransform.cpp index 835395226..a67bd2c3a 100644 --- a/libyul/backends/wasm/EWasmCodeTransform.cpp +++ b/libyul/backends/wasm/EWasmCodeTransform.cpp @@ -44,7 +44,10 @@ string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) for (auto const& statement: _ast.statements) { - yulAssert(statement.type() == typeid(yul::FunctionDefinition), ""); + yulAssert( + statement.type() == typeid(yul::FunctionDefinition), + "Expected only function definitions at the highest level." + ); functions.emplace_back(transform.translateFunction(boost::get(statement))); } @@ -57,19 +60,19 @@ wasm::Expression EWasmCodeTransform::generateMultiAssignment( ) { yulAssert(!_variableNames.empty(), ""); - wasm::LocalAssignment assignment{std::move(_variableNames.front()), std::move(_firstValue)}; + wasm::LocalAssignment assignment{move(_variableNames.front()), std::move(_firstValue)}; if (_variableNames.size() == 1) return move(assignment); wasm::Block block; - block.statements.emplace_back(std::move(assignment)); + block.statements.emplace_back(move(assignment)); for (size_t i = 1; i < _variableNames.size(); ++i) block.statements.emplace_back(wasm::LocalAssignment{ - std::move(_variableNames.at(i)), + move(_variableNames.at(i)), make_unique(wasm::GlobalVariable{m_globalVariables.at(i - 1).variableName}) }); - return std::move(block); + return move(block); } wasm::Expression EWasmCodeTransform::operator()(VariableDeclaration const& _varDecl) @@ -82,7 +85,7 @@ wasm::Expression EWasmCodeTransform::operator()(VariableDeclaration const& _varD } if (_varDecl.value) - return generateMultiAssignment(std::move(variableNames), visit(*_varDecl.value)); + return generateMultiAssignment(move(variableNames), visit(*_varDecl.value)); else return wasm::BuiltinCall{"nop", {}}; } @@ -92,7 +95,7 @@ wasm::Expression EWasmCodeTransform::operator()(Assignment const& _assignment) vector variableNames; for (auto const& var: _assignment.variableNames) variableNames.emplace_back(var.name.str()); - return generateMultiAssignment(std::move(variableNames), visit(*_assignment.value)); + return generateMultiAssignment(move(variableNames), visit(*_assignment.value)); } wasm::Expression EWasmCodeTransform::operator()(StackAssignment const&) @@ -150,13 +153,46 @@ wasm::Expression EWasmCodeTransform::operator()(yul::Instruction const&) wasm::Expression EWasmCodeTransform::operator()(If const& _if) { - return wasm::If{visit(*_if.condition), visit(_if.body.statements)}; + return wasm::If{visit(*_if.condition), visit(_if.body.statements), {}}; } -wasm::Expression EWasmCodeTransform::operator()(Switch const&) +wasm::Expression EWasmCodeTransform::operator()(Switch const& _switch) { - solUnimplementedAssert(false, ""); - return {}; + 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&) diff --git a/libyul/backends/wasm/EWasmToText.cpp b/libyul/backends/wasm/EWasmToText.cpp index 633f90dcb..a0d552d11 100644 --- a/libyul/backends/wasm/EWasmToText.cpp +++ b/libyul/backends/wasm/EWasmToText.cpp @@ -84,7 +84,10 @@ string EWasmToText::operator()(wasm::GlobalAssignment const& _assignment) string EWasmToText::operator()(wasm::If const& _if) { - return "(if " + visit(*_if.condition) + " (then\n" + indented(joinTransformed(_if.statements)) + "\n))\n"; + 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)