From f7ccdb6447371707b6c9effb09a88bc161c9fd99 Mon Sep 17 00:00:00 2001 From: mingchuan Date: Fri, 26 Apr 2019 13:39:40 +0800 Subject: [PATCH] Implement WordSizeTransform This transformation turns every u256 variable into four u64 variable. Purpose is to transpile EVMDialect yul to WasmDialect yul. --- libyul/CMakeLists.txt | 2 + libyul/backends/wasm/WordSizeTransform.cpp | 238 +++++++++++++++++++++ libyul/backends/wasm/WordSizeTransform.h | 81 +++++++ 3 files changed, 321 insertions(+) create mode 100644 libyul/backends/wasm/WordSizeTransform.cpp create mode 100644 libyul/backends/wasm/WordSizeTransform.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index b70d813b4..2e04bf9be 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -40,6 +40,8 @@ add_library(yul backends/evm/NoOutputAssembly.cpp backends/wasm/WasmDialect.cpp backends/wasm/WasmDialect.h + backends/wasm/WordSizeTransform.cpp + backends/wasm/WordSizeTransform.h optimiser/ASTCopier.cpp optimiser/ASTCopier.h optimiser/ASTWalker.cpp diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp new file mode 100644 index 000000000..d99d4cc26 --- /dev/null +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -0,0 +1,238 @@ +/* + 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 . +*/ + +#include +#include +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace yul; + +void WordSizeTransform::operator()(FunctionDefinition& _fd) +{ + rewriteVarDeclList(_fd.parameters); + rewriteVarDeclList(_fd.returnVariables); + (*this)(_fd.body); +} + +void WordSizeTransform::operator()(FunctionalInstruction& _ins) +{ + rewriteFunctionCallArguments(_ins.arguments); +} + +void WordSizeTransform::operator()(FunctionCall& _fc) +{ + rewriteFunctionCallArguments(_fc.arguments); +} + +void WordSizeTransform::operator()(If&) +{ + yulAssert(false, "If statement not implemented."); +} + +void WordSizeTransform::operator()(Switch&) +{ + yulAssert(false, "Switch statement not implemented."); +} + +void WordSizeTransform::operator()(Block& _block) +{ + iterateReplacing( + _block.statements, + [&](Statement& _s) -> boost::optional> + { + if (_s.type() == typeid(VariableDeclaration)) + { + VariableDeclaration& varDecl = boost::get(_s); + if ( + !varDecl.value || + varDecl.value->type() == typeid(FunctionalInstruction) || + varDecl.value->type() == typeid(FunctionCall) + ) + { + if (varDecl.value) visit(*varDecl.value); + rewriteVarDeclList(varDecl.variables); + return boost::none; + } + else if ( + varDecl.value->type() == typeid(Identifier) || + varDecl.value->type() == typeid(Literal) + ) + { + yulAssert(varDecl.variables.size() == 1, ""); + auto newRhs = expandValue(*varDecl.value); + auto newLhs = generateU64IdentifierNames(varDecl.variables[0].name); + vector ret; + for (int i = 0; i < 4; i++) + ret.push_back( + VariableDeclaration{ + varDecl.location, + {TypedName{varDecl.location, newLhs[i], "u64"_yulstring}}, + std::move(newRhs[i]) + } + ); + return ret; + } + else + yulAssert(false, ""); + } + else if (_s.type() == typeid(Assignment)) + { + Assignment& assignment = boost::get(_s); + yulAssert(assignment.value, ""); + if ( + assignment.value->type() == typeid(FunctionalInstruction) || + assignment.value->type() == typeid(FunctionCall) + ) + { + if (assignment.value) visit(*assignment.value); + rewriteIdentifierList(assignment.variableNames); + return boost::none; + } + else if ( + assignment.value->type() == typeid(Identifier) || + assignment.value->type() == typeid(Literal) + ) + { + yulAssert(assignment.variableNames.size() == 1, ""); + auto newRhs = expandValue(*assignment.value); + YulString lhsName = assignment.variableNames[0].name; + vector ret; + for (int i = 0; i < 4; i++) + ret.push_back( + Assignment{ + assignment.location, + {Identifier{assignment.location, m_variableMapping.at(lhsName)[i]}}, + std::move(newRhs[i]) + } + ); + return ret; + } + else + yulAssert(false, ""); + } + else + visit(_s); + return boost::none; + } + ); +} + +void WordSizeTransform::run(Block& _ast, NameDispenser& _nameDispenser) +{ + WordSizeTransform{_nameDispenser}(_ast); +} + +void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList) +{ + iterateReplacing( + _nameList, + [&](TypedName const& _n) -> boost::optional + { + TypedNameList ret; + yulAssert(m_variableMapping.find(_n.name) == m_variableMapping.end(), ""); + for (int i = 0; i < 4; i++) + { + auto newName = m_nameDispenser.newName(_n.name); + m_variableMapping[_n.name][i] = newName; + ret.push_back(TypedName{_n.location, newName, "u64"_yulstring}); + } + return ret; + } + ); +} + +void WordSizeTransform::rewriteIdentifierList(vector& _ids) +{ + iterateReplacing( + _ids, + [&](Identifier const& _id) -> boost::optional> + { + vector ret; + for (auto newId: m_variableMapping.at(_id.name)) + ret.push_back(Identifier{_id.location, newId}); + return ret; + } + ); +} + +void WordSizeTransform::rewriteFunctionCallArguments(vector& _args) +{ + iterateReplacing( + _args, + [&](Expression& _e) -> boost::optional> + { + // ExpressionSplitter guarantees arguments to be Identifier or Literal + yulAssert(_e.type() == typeid(Identifier) || _e.type() == typeid(Literal), ""); + vector ret; + for (auto& v: expandValue(_e)) + ret.push_back(*v); + return ret; + } + ); +} + +array WordSizeTransform::generateU64IdentifierNames(YulString const& _s) +{ + yulAssert(m_variableMapping.find(_s) == m_variableMapping.end(), ""); + array ret; + for (int i = 0; i < 4; i++) + { + auto newName = m_nameDispenser.newName(_s); + m_variableMapping[_s][i] = newName; + ret[i] = newName; + } + return ret; +} + +array, 4> WordSizeTransform::expandValue(Expression const& _e) +{ + array, 4> ret; + if (_e.type() == typeid(Identifier)) + { + Identifier const& id = boost::get(_e); + for (int i = 0; i < 4; i++) + ret[i] = make_unique(Identifier{id.location, m_variableMapping.at(id.name)[i]}); + } + else if (_e.type() == typeid(Literal)) + { + Literal const& lit = boost::get(_e); + u256 val = valueOfLiteral(lit); + for (int i = 3; i >= 0; i--) + { + u256 currentVal = val & std::numeric_limits::max(); + val >>= 64; + ret[i] = make_unique( + Literal{ + lit.location, + LiteralKind::Number, + YulString(currentVal.str()), + "u64"_yulstring + } + ); + } + } + else + yulAssert(false, ""); + return ret; +} + diff --git a/libyul/backends/wasm/WordSizeTransform.h b/libyul/backends/wasm/WordSizeTransform.h new file mode 100644 index 000000000..a9f480551 --- /dev/null +++ b/libyul/backends/wasm/WordSizeTransform.h @@ -0,0 +1,81 @@ +/* + 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 . +*/ +/** + * Replace every u256 variable with four u64 variables. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace yul +{ + +/** + * A stage that replace every u256 variable with four u64 variables. + * This transformation stage is required because values in EVM are 256 bits, + * but wasm only supports values up to 64 bits, so we use four u64 values to simulate + * one u256 value. + * + * For FunctionalInstruction that accepts or returns u256 values, they accepts or returns + * four times the number of values after this transformation, with the order of significance, + * from the most significant to the least significant. + * + * For example, the FunctionalInstruction MUL supplied by code generator + * should take 8 arguments and return 4 values (instead of 2 and 1) after this transformation. + * + * mul(a1, a2, a3, a4, b1, b2, b3, b4) -> c1, c2, c3, c4 + * + * the value of c4 should be + * ((a1*(2^192) + a2*(2^128) + a3(2^64) + a4) * (b1*(2^192) + b2*(2^128) + b3(2^64) + b4)) & ((1<<64)-1) + * + * Prerequisite: Disambiguator, ExpressionSplitter + */ +class WordSizeTransform: public ASTModifier +{ +public: + void operator()(FunctionDefinition&) override; + void operator()(FunctionalInstruction&) override; + void operator()(FunctionCall&) override; + void operator()(If&) override; + void operator()(Switch&) override; + void operator()(Block& _block) override; + + static void run(Block& _ast, NameDispenser& _nameDispenser); + +private: + explicit WordSizeTransform(NameDispenser& _nameDispenser): + m_nameDispenser(_nameDispenser) + { } + + void rewriteVarDeclList(std::vector&); + void rewriteIdentifierList(std::vector&); + void rewriteFunctionCallArguments(std::vector&); + + std::array generateU64IdentifierNames(YulString const& _s); + std::array, 4> expandValue(Expression const& _e); + + NameDispenser& m_nameDispenser; + /// maps original u256 variable's name to corresponding u64 variables' names + std::map> m_variableMapping; +}; + +}