Merge pull request #7601 from ethereum/binaryTransform

EWasm Binary transform
This commit is contained in:
chriseth 2019-11-05 19:59:22 +01:00 committed by GitHub
commit 3b3d19010d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 738 additions and 97 deletions

View File

@ -200,7 +200,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
Dialect const& dialect = languageToDialect(m_language, EVMVersion{}); Dialect const& dialect = languageToDialect(m_language, EVMVersion{});
MachineAssemblyObject object; MachineAssemblyObject object;
object.assembly = EWasmObjectCompiler::compile(*m_parserResult, dialect); object.assembly = EWasmObjectCompiler::compile(*m_parserResult, dialect).first;
return object; return object;
} }
} }

View File

@ -50,6 +50,8 @@ add_library(yul
backends/wasm/EWasmObjectCompiler.h backends/wasm/EWasmObjectCompiler.h
backends/wasm/EWasmToText.cpp backends/wasm/EWasmToText.cpp
backends/wasm/EWasmToText.h backends/wasm/EWasmToText.h
backends/wasm/BinaryTransform.cpp
backends/wasm/BinaryTransform.h
backends/wasm/WasmDialect.cpp backends/wasm/WasmDialect.cpp
backends/wasm/WasmDialect.h backends/wasm/WasmDialect.h
backends/wasm/WordSizeTransform.cpp backends/wasm/WordSizeTransform.cpp

View File

@ -0,0 +1,582 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* EWasm to binary encoder.
*/
#include <libyul/backends/wasm/BinaryTransform.h>
#include <libyul/Exceptions.h>
#include <libdevcore/CommonData.h>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace yul;
using namespace dev;
using namespace yul::wasm;
namespace
{
bytes toBytes(uint8_t _b)
{
return bytes(1, _b);
}
enum class Section: uint8_t
{
CUSTOM = 0x00,
TYPE = 0x01,
IMPORT = 0x02,
FUNCTION = 0x03,
MEMORY = 0x05,
GLOBAL = 0x06,
EXPORT = 0x07,
CODE = 0x0a
};
bytes toBytes(Section _s)
{
return toBytes(uint8_t(_s));
}
enum class ValueType: uint8_t
{
Void = 0x40,
Function = 0x60,
I64 = 0x7e,
I32 = 0x7f
};
bytes toBytes(ValueType _vt)
{
return toBytes(uint8_t(_vt));
}
enum class Export: uint8_t
{
Function = 0x0,
Memory = 0x2
};
bytes toBytes(Export _export)
{
return toBytes(uint8_t(_export));
}
enum class Opcode: uint8_t
{
Unreachable = 0x00,
Nop = 0x01,
Block = 0x02,
Loop = 0x03,
If = 0x04,
Else = 0x05,
Try = 0x06,
Catch = 0x07,
Throw = 0x08,
Rethrow = 0x09,
BrOnExn = 0x0a,
End = 0x0b,
Br = 0x0c,
BrIf = 0x0d,
BrTable = 0x0e,
Return = 0x0f,
Call = 0x10,
CallIndirect = 0x11,
ReturnCall = 0x12,
ReturnCallIndirect = 0x13,
Drop = 0x1a,
Select = 0x1b,
LocalGet = 0x20,
LocalSet = 0x21,
LocalTee = 0x22,
GlobalGet = 0x23,
GlobalSet = 0x24,
I32Const = 0x41,
I64Const = 0x42,
};
bytes toBytes(Opcode _o)
{
return toBytes(uint8_t(_o));
}
static std::map<string, uint8_t> const builtins = {
{"i32.load", 0x28},
{"i64.load", 0x29},
{"i32.load8_s", 0x2c},
{"i32.load8_u", 0x2d},
{"i32.load16_s", 0x2e},
{"i32.load16_u", 0x2f},
{"i64.load8_s", 0x30},
{"i64.load8_u", 0x31},
{"i64.load16_s", 0x32},
{"i64.load16_u", 0x33},
{"i64.load32_s", 0x34},
{"i64.load32_u", 0x35},
{"i32.store", 0x36},
{"i64.store", 0x37},
{"i32.store8", 0x3a},
{"i32.store16", 0x3b},
{"i64.store8", 0x3c},
{"i64.store16", 0x3d},
{"i64.store32", 0x3e},
{"memory.size", 0x3f},
{"memory.grow", 0x40},
{"i32.eqz", 0x45},
{"i32.eq", 0x46},
{"i32.ne", 0x47},
{"i32.lt_s", 0x48},
{"i32.lt_u", 0x49},
{"i32.gt_s", 0x4a},
{"i32.gt_u", 0x4b},
{"i32.le_s", 0x4c},
{"i32.le_u", 0x4d},
{"i32.ge_s", 0x4e},
{"i32.ge_u", 0x4f},
{"i64.eqz", 0x50},
{"i64.eq", 0x51},
{"i64.ne", 0x52},
{"i64.lt_s", 0x53},
{"i64.lt_u", 0x54},
{"i64.gt_s", 0x55},
{"i64.gt_u", 0x56},
{"i64.le_s", 0x57},
{"i64.le_u", 0x58},
{"i64.ge_s", 0x59},
{"i64.ge_u", 0x5a},
{"i32.clz", 0x67},
{"i32.ctz", 0x68},
{"i32.popcnt", 0x69},
{"i32.add", 0x6a},
{"i32.sub", 0x6b},
{"i32.mul", 0x6c},
{"i32.div_s", 0x6d},
{"i32.div_u", 0x6e},
{"i32.rem_s", 0x6f},
{"i32.rem_u", 0x70},
{"i32.and", 0x71},
{"i32.or", 0x72},
{"i32.xor", 0x73},
{"i32.shl", 0x74},
{"i32.shr_s", 0x75},
{"i32.shr_u", 0x76},
{"i32.rotl", 0x77},
{"i32.rotr", 0x78},
{"i64.clz", 0x79},
{"i64.ctz", 0x7a},
{"i64.popcnt", 0x7b},
{"i64.add", 0x7c},
{"i64.sub", 0x7d},
{"i64.mul", 0x7e},
{"i64.div_s", 0x7f},
{"i64.div_u", 0x80},
{"i64.rem_s", 0x81},
{"i64.rem_u", 0x82},
{"i64.and", 0x83},
{"i64.or", 0x84},
{"i64.xor", 0x85},
{"i64.shl", 0x86},
{"i64.shr_s", 0x87},
{"i64.shr_u", 0x88},
{"i64.rotl", 0x89},
{"i64.rotr", 0x8a},
{"i32.wrap_i64", 0xa7},
{"i64.extend_i32_s", 0xac},
{"i64.extend_i32_u", 0xad},
};
bytes lebEncode(uint64_t _n)
{
bytes encoded;
while (_n > 0x7f)
{
encoded.emplace_back(uint8_t(0x80 | (_n & 0x7f)));
_n >>= 7;
}
encoded.emplace_back(_n);
return encoded;
}
bytes lebEncodeSigned(int64_t _n)
{
if (_n >= 0 && _n < 0x40)
return toBytes(uint8_t(uint64_t(_n) & 0xff));
else if (-_n > 0 && -_n < 0x40)
return toBytes(uint8_t(uint64_t(_n + 0x80) & 0xff));
else
return toBytes(uint8_t(0x80 | uint8_t(_n & 0x7f))) + lebEncodeSigned(_n / 0x80);
}
bytes prefixSize(bytes _data)
{
size_t size = _data.size();
return lebEncode(size) + std::move(_data);
}
bytes makeSection(Section _section, bytes _data)
{
return toBytes(_section) + prefixSize(std::move(_data));
}
}
bytes BinaryTransform::run(Module const& _module)
{
BinaryTransform bt;
for (size_t i = 0; i < _module.globals.size(); ++i)
bt.m_globals[_module.globals[i].variableName] = i;
size_t funID = 0;
for (FunctionImport const& fun: _module.imports)
bt.m_functions[fun.internalName] = funID++;
for (FunctionDefinition const& fun: _module.functions)
bt.m_functions[fun.name] = funID++;
bytes ret{0, 'a', 's', 'm'};
// version
ret += bytes{1, 0, 0, 0};
ret += bt.typeSection(_module.imports, _module.functions);
ret += bt.importSection(_module.imports);
ret += bt.functionSection(_module.functions);
ret += bt.memorySection();
ret += bt.globalSection();
ret += bt.exportSection();
for (auto const& sub: _module.subModules)
{
// TODO should we prefix and / or shorten the name?
bytes data = BinaryTransform::run(sub.second);
size_t length = data.size();
ret += bt.customSection(sub.first, std::move(data));
bt.m_subModulePosAndSize[sub.first] = {ret.size() - length, length};
}
ret += bt.codeSection(_module.functions);
return ret;
}
bytes BinaryTransform::operator()(Literal const& _literal)
{
return toBytes(Opcode::I64Const) + lebEncodeSigned(_literal.value);
}
bytes BinaryTransform::operator()(StringLiteral const&)
{
// TODO is this used?
yulAssert(false, "String literals not yet implemented");
}
bytes BinaryTransform::operator()(LocalVariable const& _variable)
{
return toBytes(Opcode::LocalGet) + lebEncode(m_locals.at(_variable.name));
}
bytes BinaryTransform::operator()(GlobalVariable const& _variable)
{
return toBytes(Opcode::GlobalGet) + lebEncode(m_globals.at(_variable.name));
}
bytes BinaryTransform::operator()(BuiltinCall const& _call)
{
// We need to avoid visiting the arguments of `dataoffset` and `datasize` because
// they are references to object names that should not end up in the code.
if (_call.functionName == "dataoffset")
{
string name = boost::get<StringLiteral>(_call.arguments.at(0)).value;
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first);
}
else if (_call.functionName == "datasize")
{
string name = boost::get<StringLiteral>(_call.arguments.at(0)).value;
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second);
}
bytes args = visit(_call.arguments);
if (_call.functionName == "unreachable")
return toBytes(Opcode::Unreachable);
else
{
yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found");
bytes ret = std::move(args) + toBytes(builtins.at(_call.functionName));
if (
_call.functionName.find(".load") != string::npos ||
_call.functionName.find(".store") != string::npos
)
// alignment and offset
ret += bytes{{3, 0}};
return ret;
}
}
bytes BinaryTransform::operator()(FunctionCall const& _call)
{
return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functions.at(_call.functionName));
}
bytes BinaryTransform::operator()(LocalAssignment const& _assignment)
{
return
boost::apply_visitor(*this, *_assignment.value) +
toBytes(Opcode::LocalSet) +
lebEncode(m_locals.at(_assignment.variableName));
}
bytes BinaryTransform::operator()(GlobalAssignment const& _assignment)
{
return
boost::apply_visitor(*this, *_assignment.value) +
toBytes(Opcode::GlobalSet) +
lebEncode(m_globals.at(_assignment.variableName));
}
bytes BinaryTransform::operator()(If const& _if)
{
bytes result =
boost::apply_visitor(*this, *_if.condition) +
toBytes(Opcode::If) +
toBytes(ValueType::Void);
m_labels.push({});
result += visit(_if.statements);
if (_if.elseStatements)
result += toBytes(Opcode::Else) + visit(*_if.elseStatements);
m_labels.pop();
result += toBytes(Opcode::End);
return result;
}
bytes BinaryTransform::operator()(Loop const& _loop)
{
bytes result = toBytes(Opcode::Loop) + toBytes(ValueType::Void);
m_labels.push(_loop.labelName);
result += visit(_loop.statements);
m_labels.pop();
result += toBytes(Opcode::End);
return result;
}
bytes BinaryTransform::operator()(Break const&)
{
yulAssert(false, "br not yet implemented.");
// TODO the index is just the nesting depth.
return {};
}
bytes BinaryTransform::operator()(BreakIf const&)
{
yulAssert(false, "br_if not yet implemented.");
// TODO the index is just the nesting depth.
return {};
}
bytes BinaryTransform::operator()(Block const& _block)
{
return
toBytes(Opcode::Block) +
toBytes(ValueType::Void) +
visit(_block.statements) +
toBytes(Opcode::End);
}
bytes BinaryTransform::operator()(FunctionDefinition const& _function)
{
bytes ret;
// This is a kind of run-length-encoding of local types. Has to be adapted once
// we have locals of different types.
ret += lebEncode(1); // number of locals groups
ret += lebEncode(_function.locals.size());
ret += toBytes(ValueType::I64);
m_locals.clear();
size_t varIdx = 0;
for (size_t i = 0; i < _function.parameterNames.size(); ++i)
m_locals[_function.parameterNames[i]] = varIdx++;
for (size_t i = 0; i < _function.locals.size(); ++i)
m_locals[_function.locals[i].variableName] = varIdx++;
ret += visit(_function.body);
ret += toBytes(Opcode::End);
return prefixSize(std::move(ret));
}
BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import)
{
return {
encodeTypes(_import.paramTypes),
encodeTypes(_import.returnType ? vector<string>(1, *_import.returnType) : vector<string>())
};
}
BinaryTransform::Type BinaryTransform::typeOf(FunctionDefinition const& _funDef)
{
return {
encodeTypes(vector<string>(_funDef.parameterNames.size(), "i64")),
encodeTypes(vector<string>(_funDef.returns ? 1 : 0, "i64"))
};
}
uint8_t BinaryTransform::encodeType(string const& _typeName)
{
if (_typeName == "i32")
return uint8_t(ValueType::I32);
else if (_typeName == "i64")
return uint8_t(ValueType::I64);
else
yulAssert(false, "");
return 0;
}
vector<uint8_t> BinaryTransform::encodeTypes(vector<string> const& _typeNames)
{
vector<uint8_t> result;
for (auto const& t: _typeNames)
result.emplace_back(encodeType(t));
return result;
}
bytes BinaryTransform::typeSection(
vector<FunctionImport> const& _imports,
vector<FunctionDefinition> const& _functions
)
{
map<Type, vector<string>> types;
for (auto const& import: _imports)
types[typeOf(import)].emplace_back(import.internalName);
for (auto const& fun: _functions)
types[typeOf(fun)].emplace_back(fun.name);
bytes result;
size_t index = 0;
for (auto const& [type, funNames]: types)
{
for (string const& name: funNames)
m_functionTypes[name] = index;
result += toBytes(ValueType::Function);
result += lebEncode(type.first.size()) + type.first;
result += lebEncode(type.second.size()) + type.second;
index++;
}
return makeSection(Section::TYPE, lebEncode(index) + std::move(result));
}
bytes BinaryTransform::importSection(
vector<FunctionImport> const& _imports
)
{
bytes result = lebEncode(_imports.size());
for (FunctionImport const& import: _imports)
{
uint8_t importKind = 0; // function
result +=
encodeName(import.module) +
encodeName(import.externalName) +
toBytes(importKind) +
lebEncode(m_functionTypes[import.internalName]);
}
return makeSection(Section::IMPORT, std::move(result));
}
bytes BinaryTransform::functionSection(vector<FunctionDefinition> const& _functions)
{
bytes result = lebEncode(_functions.size());
for (auto const& fun: _functions)
result += lebEncode(m_functionTypes.at(fun.name));
return makeSection(Section::FUNCTION, std::move(result));
}
bytes BinaryTransform::memorySection()
{
bytes result = lebEncode(1);
result.push_back(0); // flags
result.push_back(1); // initial
return makeSection(Section::MEMORY, std::move(result));
}
bytes BinaryTransform::globalSection()
{
bytes result = lebEncode(m_globals.size());
for (size_t i = 0; i < m_globals.size(); ++i)
result +=
// mutable i64
bytes{uint8_t(ValueType::I64), 1} +
toBytes(Opcode::I64Const) +
lebEncodeSigned(0) +
toBytes(Opcode::End);
return makeSection(Section::GLOBAL, std::move(result));
}
bytes BinaryTransform::exportSection()
{
bytes result = lebEncode(2);
result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0);
result += encodeName("main") + toBytes(Export::Function) + lebEncode(m_functions.at("main"));
return makeSection(Section::EXPORT, std::move(result));
}
bytes BinaryTransform::customSection(string const& _name, bytes _data)
{
bytes result = encodeName(_name) + std::move(_data);
return makeSection(Section::CUSTOM, std::move(result));
}
bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _functions)
{
bytes result = lebEncode(_functions.size());
for (FunctionDefinition const& fun: _functions)
result += (*this)(fun);
return makeSection(Section::CODE, std::move(result));
}
bytes BinaryTransform::visit(vector<Expression> const& _expressions)
{
bytes result;
for (auto const& expr: _expressions)
result += boost::apply_visitor(*this, expr);
return result;
}
bytes BinaryTransform::visitReversed(vector<Expression> const& _expressions)
{
bytes result;
for (auto const& expr: _expressions | boost::adaptors::reversed)
result += boost::apply_visitor(*this, expr);
return result;
}
bytes BinaryTransform::encodeName(std::string const& _name)
{
// UTF-8 is allowed here by the Wasm spec, but since all names here should stem from
// Solidity or Yul identifiers or similar, non-ascii characters ending up here
// is a very bad sign.
for (char c: _name)
yulAssert(uint8_t(c) <= 0x7f, "Non-ascii character found.");
return lebEncode(_name.size()) + asBytes(_name);
}

View File

@ -0,0 +1,94 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* EWasm to binary encoder.
*/
#pragma once
#include <libyul/backends/wasm/EWasmAST.h>
#include <libdevcore/Common.h>
#include <vector>
#include <stack>
namespace yul
{
namespace wasm
{
/**
* Web assembly to binary transform.
*/
class BinaryTransform: public boost::static_visitor<dev::bytes>
{
public:
static dev::bytes run(Module const& _module);
dev::bytes operator()(wasm::Literal const& _literal);
dev::bytes operator()(wasm::StringLiteral const& _literal);
dev::bytes operator()(wasm::LocalVariable const& _identifier);
dev::bytes operator()(wasm::GlobalVariable const& _identifier);
dev::bytes operator()(wasm::BuiltinCall const& _builinCall);
dev::bytes operator()(wasm::FunctionCall const& _functionCall);
dev::bytes operator()(wasm::LocalAssignment const& _assignment);
dev::bytes operator()(wasm::GlobalAssignment const& _assignment);
dev::bytes operator()(wasm::If const& _if);
dev::bytes operator()(wasm::Loop const& _loop);
dev::bytes operator()(wasm::Break const& _break);
dev::bytes operator()(wasm::BreakIf const& _break);
dev::bytes operator()(wasm::Block const& _block);
dev::bytes operator()(wasm::FunctionDefinition const& _function);
private:
using Type = std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>>;
static Type typeOf(wasm::FunctionImport const& _import);
static Type typeOf(wasm::FunctionDefinition const& _funDef);
static uint8_t encodeType(std::string const& _typeName);
static std::vector<uint8_t> encodeTypes(std::vector<std::string> const& _typeNames);
dev::bytes typeSection(
std::vector<wasm::FunctionImport> const& _imports,
std::vector<wasm::FunctionDefinition> const& _functions
);
dev::bytes importSection(std::vector<wasm::FunctionImport> const& _imports);
dev::bytes functionSection(std::vector<wasm::FunctionDefinition> const& _functions);
dev::bytes memorySection();
dev::bytes globalSection();
dev::bytes exportSection();
dev::bytes customSection(std::string const& _name, dev::bytes _data);
dev::bytes codeSection(std::vector<wasm::FunctionDefinition> const& _functions);
dev::bytes visit(std::vector<wasm::Expression> const& _expressions);
dev::bytes visitReversed(std::vector<wasm::Expression> const& _expressions);
static dev::bytes encodeName(std::string const& _name);
std::map<std::string, size_t> m_locals;
std::map<std::string, size_t> m_globals;
std::map<std::string, size_t> m_functions;
std::map<std::string, size_t> m_functionTypes;
std::stack<std::string> m_labels;
std::map<std::string, std::pair<size_t, size_t>> m_subModulePosAndSize;
};
}
}

View File

@ -23,6 +23,8 @@
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include <memory>
namespace yul namespace yul
{ {
@ -86,6 +88,16 @@ struct FunctionDefinition
std::vector<Expression> body; std::vector<Expression> body;
}; };
/**
* Abstract representation of a wasm module.
*/
struct Module
{
std::vector<GlobalVariableDeclaration> globals;
std::vector<FunctionImport> imports;
std::vector<FunctionDefinition> functions;
std::map<std::string, Module> subModules;
};
} }
} }

View File

@ -20,7 +20,6 @@
#include <libyul/backends/wasm/EWasmCodeTransform.h> #include <libyul/backends/wasm/EWasmCodeTransform.h>
#include <libyul/backends/wasm/EWasmToText.h>
#include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/NameCollector.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
@ -37,10 +36,11 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) wasm::Module EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
{ {
wasm::Module module;
EWasmCodeTransform transform(_dialect, _ast); EWasmCodeTransform transform(_dialect, _ast);
vector<wasm::FunctionDefinition> functions;
for (auto const& statement: _ast.statements) for (auto const& statement: _ast.statements)
{ {
@ -49,17 +49,14 @@ string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
"Expected only function definitions at the highest level." "Expected only function definitions at the highest level."
); );
if (statement.type() == typeid(yul::FunctionDefinition)) if (statement.type() == typeid(yul::FunctionDefinition))
functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement))); module.functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement)));
} }
std::vector<wasm::FunctionImport> imports;
for (auto& imp: transform.m_functionsToImport) for (auto& imp: transform.m_functionsToImport)
imports.emplace_back(std::move(imp.second)); module.imports.emplace_back(std::move(imp.second));
return EWasmToText().run( module.globals = transform.m_globalVariables;
transform.m_globalVariables,
imports, return module;
functions
);
} }
wasm::Expression EWasmCodeTransform::generateMultiAssignment( wasm::Expression EWasmCodeTransform::generateMultiAssignment(

View File

@ -35,7 +35,7 @@ struct AsmAnalysisInfo;
class EWasmCodeTransform: public boost::static_visitor<wasm::Expression> class EWasmCodeTransform: public boost::static_visitor<wasm::Expression>
{ {
public: public:
static std::string run(Dialect const& _dialect, yul::Block const& _ast); static wasm::Module run(Dialect const& _dialect, yul::Block const& _ast);
public: public:
wasm::Expression operator()(yul::Instruction const& _instruction); wasm::Expression operator()(yul::Instruction const& _instruction);

View File

@ -21,32 +21,36 @@
#include <libyul/backends/wasm/EWasmObjectCompiler.h> #include <libyul/backends/wasm/EWasmObjectCompiler.h>
#include <libyul/backends/wasm/EWasmCodeTransform.h> #include <libyul/backends/wasm/EWasmCodeTransform.h>
#include <libyul/backends/wasm/BinaryTransform.h>
#include <libyul/backends/wasm/EWasmToText.h>
#include <libyul/Object.h> #include <libyul/Object.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <libdevcore/CommonData.h>
using namespace yul; using namespace yul;
using namespace std; using namespace std;
string EWasmObjectCompiler::compile(Object& _object, Dialect const& _dialect) pair<string, dev::bytes> EWasmObjectCompiler::compile(Object& _object, Dialect const& _dialect)
{ {
EWasmObjectCompiler compiler(_dialect); EWasmObjectCompiler compiler(_dialect);
return compiler.run(_object); wasm::Module module = compiler.run(_object);
return {EWasmToText().run(module), wasm::BinaryTransform::run(module)};
} }
string EWasmObjectCompiler::run(Object& _object) wasm::Module EWasmObjectCompiler::run(Object& _object)
{ {
string ret; yulAssert(_object.analysisInfo, "No analysis info.");
yulAssert(_object.code, "No code.");
wasm::Module module = EWasmCodeTransform::run(m_dialect, *_object.code);
for (auto& subNode: _object.subObjects) for (auto& subNode: _object.subObjects)
if (Object* subObject = dynamic_cast<Object*>(subNode.get())) if (Object* subObject = dynamic_cast<Object*>(subNode.get()))
ret += compile(*subObject, m_dialect); module.subModules[subObject->name.str()] = run(*subObject);
else else
yulAssert(false, "Data is not yet supported for EWasm."); yulAssert(false, "Data is not yet supported for EWasm.");
yulAssert(_object.analysisInfo, "No analysis info."); return module;
yulAssert(_object.code, "No code.");
ret += EWasmCodeTransform::run(m_dialect, *_object.code);
return ret;
} }

View File

@ -21,22 +21,33 @@
#pragma once #pragma once
#include <string> #include <string>
#include <vector>
#include <tuple>
namespace dev
{
using bytes = std::vector<uint8_t>;
}
namespace yul namespace yul
{ {
struct Object; struct Object;
struct Dialect; struct Dialect;
namespace wasm
{
struct Module;
}
class EWasmObjectCompiler class EWasmObjectCompiler
{ {
public: public:
static std::string compile(Object& _object, Dialect const& _dialect); /// Compiles the given object and returns the WAST and the binary representation.
static std::pair<std::string, dev::bytes> compile(Object& _object, Dialect const& _dialect);
private: private:
EWasmObjectCompiler(Dialect const& _dialect): EWasmObjectCompiler(Dialect const& _dialect):
m_dialect(_dialect) m_dialect(_dialect)
{} {}
std::string run(Object& _object); wasm::Module run(Object& _object);
Dialect const& m_dialect; Dialect const& m_dialect;
}; };

View File

@ -30,14 +30,15 @@ using namespace std;
using namespace yul; using namespace yul;
using namespace dev; using namespace dev;
string EWasmToText::run( string EWasmToText::run(wasm::Module const& _module)
vector<wasm::GlobalVariableDeclaration> const& _globals,
vector<wasm::FunctionImport> const& _imports,
vector<wasm::FunctionDefinition> const& _functions
)
{ {
string ret = "(module\n"; string ret = "(module\n";
for (wasm::FunctionImport const& imp: _imports) for (auto const& sub: _module.subModules)
ret +=
" ;; sub-module \"" +
sub.first +
"\" will be encoded as custom section in binary here, but is skipped in text mode.\n";
for (wasm::FunctionImport const& imp: _module.imports)
{ {
ret += " (import \"" + imp.module + "\" \"" + imp.externalName + "\" (func $" + imp.internalName; ret += " (import \"" + imp.module + "\" \"" + imp.externalName + "\" (func $" + imp.internalName;
if (!imp.paramTypes.empty()) if (!imp.paramTypes.empty())
@ -52,10 +53,10 @@ string EWasmToText::run(
// export the main function // export the main function
ret += " (export \"main\" (func $main))\n"; ret += " (export \"main\" (func $main))\n";
for (auto const& g: _globals) for (auto const& g: _module.globals)
ret += " (global $" + g.variableName + " (mut i64) (i64.const 0))\n"; ret += " (global $" + g.variableName + " (mut i64) (i64.const 0))\n";
ret += "\n"; ret += "\n";
for (auto const& f: _functions) for (auto const& f: _module.functions)
ret += transform(f) + "\n"; ret += transform(f) + "\n";
return move(ret) + ")\n"; return move(ret) + ")\n";
} }

View File

@ -31,11 +31,7 @@ struct AsmAnalysisInfo;
class EWasmToText: public boost::static_visitor<std::string> class EWasmToText: public boost::static_visitor<std::string>
{ {
public: public:
std::string run( std::string run(wasm::Module const& _module);
std::vector<wasm::GlobalVariableDeclaration> const& _globals,
std::vector<wasm::FunctionImport> const& _imports,
std::vector<wasm::FunctionDefinition> const& _functions
);
public: public:
std::string operator()(wasm::Literal const& _literal); std::string operator()(wasm::Literal const& _literal);

View File

@ -1,63 +1,5 @@
{"contracts":{"A":{"C":{"ewasm":{"wast":"(module {"contracts":{"A":{"C":{"ewasm":{"wast":"(module
(import \"ethereum\" \"revert\" (func $eth.revert (param i32 i32))) ;; sub-module \"C_2_deployed\" will be encoded as custom section in binary here, but is skipped in text mode.
(memory $memory (export \"memory\") 1)
(export \"main\" (func $main))
(func $main
(local $_1 i64)
(local $_2 i64)
(local $_3 i64)
(local $hi i64)
(local $y i64)
(local $hi_1 i64)
(local.set $_1 (i64.const 0))
(local.set $_2 (i64.add (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 64)) (i64.const 64)))
(local.set $_3 (i64.const 65280))
(local.set $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (local.get $_1) (i64.const 8)) (local.get $_3)) (i64.and (i64.shr_u (local.get $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32)))
(local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32)))))
(i64.store (i32.wrap_i64 (local.get $_2)) (local.get $y))
(i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 8))) (local.get $y))
(i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 16))) (local.get $y))
(local.set $hi_1 (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (i64.const 128) (i64.const 8)) (local.get $_3)) (i64.and (i64.shr_u (i64.const 128) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (i64.const 128) (i64.const 16)))) (i64.const 32)))
(i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 24))) (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32)))))
(call $eth.revert (i32.wrap_i64 (i64.add (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (i64.const 64))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))))
)
(func $u256_to_i32
(param $x1 i64)
(param $x2 i64)
(param $x3 i64)
(param $x4 i64)
(result i64)
(local $v i64)
(if (i64.ne (i64.extend_i32_u (i64.ne (local.get $v) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3)))) (i64.const 0)) (then
(unreachable)))
(if (i64.ne (i64.extend_i32_u (i64.ne (local.get $v) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then
(unreachable)))
(local.set $v (local.get $x4))
(local.get $v)
)
(func $endian_swap_16
(param $x i64)
(result i64)
(local $y i64)
(local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255))))
(local.get $y)
)
(func $endian_swap_32
(param $x i64)
(result i64)
(local $y i64)
(local $hi i64)
(local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16)))
(local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16)))))
(local.get $y)
)
)
(module
(import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32))) (import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32)))
(import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32))) (import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32)))
(memory $memory (export \"memory\") 1) (memory $memory (export \"memory\") 1)