mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7601 from ethereum/binaryTransform
EWasm Binary transform
This commit is contained in:
commit
3b3d19010d
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
582
libyul/backends/wasm/BinaryTransform.cpp
Normal file
582
libyul/backends/wasm/BinaryTransform.cpp
Normal 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);
|
||||||
|
}
|
94
libyul/backends/wasm/BinaryTransform.h
Normal file
94
libyul/backends/wasm/BinaryTransform.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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";
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user