mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7079 from ethereum/moreWorkOnEWasmExternals
Add eWasm externals.
This commit is contained in:
commit
35ca6ef5ed
@ -268,61 +268,96 @@ function signextend(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
}
|
||||
|
||||
function u256_to_i64(x1, x2, x3, x4) -> v {
|
||||
if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() }
|
||||
v := x4
|
||||
}
|
||||
|
||||
function u256_to_i32(x1, x2, x3, x4) -> v {
|
||||
if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() }
|
||||
if i64.ne(0, i64.shr_u(x4, 32)) { invalid() }
|
||||
v := x4
|
||||
}
|
||||
|
||||
function u256_to_i32ptr(x1, x2, x3, x4) -> v {
|
||||
v := u256_to_i32(x1, x2, x3, x4)
|
||||
}
|
||||
|
||||
function keccak256(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
}
|
||||
|
||||
function address() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
||||
eth.getAddress(0)
|
||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
||||
restore_temp_mem_32(t1, t2, t3, t4)
|
||||
}
|
||||
function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
}
|
||||
function origin() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
||||
eth.getTxOrigin(0)
|
||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
||||
restore_temp_mem_32(t1, t2, t3, t4)
|
||||
}
|
||||
function caller() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
||||
eth.getCaller(0)
|
||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
||||
restore_temp_mem_32(t1, t2, t3, t4)
|
||||
}
|
||||
function callvalue() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
||||
eth.getCallValue(0)
|
||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
||||
restore_temp_mem_32(t1, t2, t3, t4)
|
||||
}
|
||||
function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
||||
eth.callDataCopy(0, u256_to_i32(x1, x2, x3, x4), 32)
|
||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
||||
restore_temp_mem_32(t1, t2, t3, t4)
|
||||
}
|
||||
function calldatasize() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
z4 := eth.getCallDataSize()
|
||||
}
|
||||
function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
eth.callDataCopy(
|
||||
u256_to_i32ptr(x1, x2, x3, x4),
|
||||
u256_to_i32(y1, y2, y3, y4),
|
||||
u256_to_i32(z1, z2, z3, z4)
|
||||
)
|
||||
}
|
||||
|
||||
// Needed?
|
||||
function codesize() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
||||
eth.getCodeSize(0)
|
||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
||||
restore_temp_mem_32(t1, t2, t3, t4)
|
||||
}
|
||||
function codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
eth.codeCopy(
|
||||
u256_to_i32ptr(x1, x2, x3, x4),
|
||||
u256_to_i32(y1, y2, y3, y4),
|
||||
u256_to_i32(z1, z2, z3, z4)
|
||||
)
|
||||
}
|
||||
function datacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
// TODO correct?
|
||||
codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4)
|
||||
}
|
||||
|
||||
function gasprice() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
||||
eth.getTxGasPrice(0)
|
||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
||||
restore_temp_mem_32(t1, t2, t3, t4)
|
||||
}
|
||||
function extcodesize(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
@ -338,12 +373,14 @@ function extcodecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
||||
}
|
||||
|
||||
function returndatasize() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
z4 := eth.getReturnDataSize()
|
||||
}
|
||||
function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
eth.returnDataCopy(
|
||||
u256_to_i32ptr(x1, x2, x3, x4),
|
||||
u256_to_i32(y1, y2, y3, y4),
|
||||
u256_to_i32(z1, z2, z3, z4)
|
||||
)
|
||||
}
|
||||
|
||||
function blockhash(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||
@ -355,34 +392,87 @@ function coinbase() -> z1, z2, z3, z4 {
|
||||
unreachable()
|
||||
}
|
||||
function timestamp() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
z4 := eth.getBlockTimestamp()
|
||||
}
|
||||
function number() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
z4 := eth.getBlockNumber()
|
||||
}
|
||||
function difficulty() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
||||
eth.getBlockDifficulty(0)
|
||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
||||
restore_temp_mem_32(t1, t2, t3, t4)
|
||||
}
|
||||
function gaslimit() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
z4 := eth.getBlockGasLimit()
|
||||
}
|
||||
|
||||
function pop(x1, x2, x3, x4) {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
}
|
||||
|
||||
|
||||
function endian_swap_16(x) -> y {
|
||||
let hi := i64.and(i64.shl(x, 8), 0xff00)
|
||||
let lo := i64.and(i64.shr_u(x, 8), 0xff)
|
||||
y := i64.or(hi, lo)
|
||||
}
|
||||
|
||||
function endian_swap_32(x) -> y {
|
||||
let hi := i64.shl(endian_swap_16(x), 16)
|
||||
let lo := endian_swap_16(i64.shr_u(x, 16))
|
||||
y := i64.or(hi, lo)
|
||||
}
|
||||
|
||||
function endian_swap(x) -> y {
|
||||
let hi := i64.shl(endian_swap_32(x), 32)
|
||||
let lo := endian_swap_32(i64.shr_u(x, 32))
|
||||
y := i64.or(hi, lo)
|
||||
}
|
||||
function save_temp_mem_32() -> t1, t2, t3, t4 {
|
||||
t1 := i64.load(0)
|
||||
t2 := i64.load(8)
|
||||
t3 := i64.load(16)
|
||||
t4 := i64.load(24)
|
||||
}
|
||||
function restore_temp_mem_32(t1, t2, t3, t4) {
|
||||
i64.store(0, t1)
|
||||
i64.store(8, t2)
|
||||
i64.store(16, t3)
|
||||
i64.store(24, t4)
|
||||
}
|
||||
function save_temp_mem_64() -> t1, t2, t3, t4, t5, t6, t7, t8 {
|
||||
t1 := i64.load(0)
|
||||
t2 := i64.load(8)
|
||||
t3 := i64.load(16)
|
||||
t4 := i64.load(24)
|
||||
t5 := i64.load(32)
|
||||
t6 := i64.load(40)
|
||||
t7 := i64.load(48)
|
||||
t8 := i64.load(54)
|
||||
}
|
||||
function restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) {
|
||||
i64.store(0, t1)
|
||||
i64.store(8, t2)
|
||||
i64.store(16, t3)
|
||||
i64.store(24, t4)
|
||||
i64.store(32, t5)
|
||||
i64.store(40, t6)
|
||||
i64.store(48, t7)
|
||||
i64.store(54, t8)
|
||||
}
|
||||
function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let pos := u256_to_i32ptr(x1, x2, x3, x4)
|
||||
z1 := endian_swap(i64.load(pos))
|
||||
z2 := endian_swap(i64.load(i64.add(pos, 8)))
|
||||
z3 := endian_swap(i64.load(i64.add(pos, 16)))
|
||||
z4 := endian_swap(i64.load(i64.add(pos, 24)))
|
||||
}
|
||||
function mstore(x1, x2, x3, x4, y1, y2, y3, y4) {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let pos := u256_to_i32ptr(x1, x2, x3, x4)
|
||||
i64.store(pos, endian_swap(x1))
|
||||
i64.store(i64.add(pos, 8), endian_swap(x2))
|
||||
i64.store(i64.add(pos, 16), endian_swap(x3))
|
||||
i64.store(i64.add(pos, 24), endian_swap(x4))
|
||||
}
|
||||
function mstore8(x1, x2, x3, x4, y1, y2, y3, y4) {
|
||||
// TODO implement
|
||||
@ -394,12 +484,19 @@ function msize() -> z1, z2, z3, z4 {
|
||||
unreachable()
|
||||
}
|
||||
function sload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let t1, t2, t3, t4, t5, t6, t7, t8 := save_temp_mem_64()
|
||||
mstore(0, 0, 0, 0, x1, x2, x3, x4)
|
||||
eth.storageLoad(0, 16)
|
||||
z1, z2, z3, z4 := mload(0, 0, 0, 16)
|
||||
restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8)
|
||||
}
|
||||
|
||||
function sstore(x1, x2, x3, x4, y1, y2, y3, y4) {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
let t1, t2, t3, t4, t5, t6, t7, t8 := save_temp_mem_64()
|
||||
mstore(0, 0, 0, 0, x1, x2, x3, x4)
|
||||
mstore(0, 0, 0, 32, y1, y2, y3, y4)
|
||||
eth.storageStore(0, 32)
|
||||
restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8)
|
||||
}
|
||||
|
||||
// Needed?
|
||||
@ -408,8 +505,7 @@ function pc() -> z1, z2, z3, z4 {
|
||||
unreachable()
|
||||
}
|
||||
function gas() -> z1, z2, z3, z4 {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
z4 := eth.getGasLeft()
|
||||
}
|
||||
|
||||
function log0(p1, p2, p3, p4, s1, s2, s3, s4) {
|
||||
@ -511,17 +607,22 @@ function create2(
|
||||
unreachable()
|
||||
}
|
||||
function selfdestruct(a1, a2, a3, a4) {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
mstore(0, 0, 0, 0, a1, a2, a3, a4)
|
||||
// In EVM, addresses are padded to 32 bytes, so discard the first 12.
|
||||
eth.selfDestruct(12)
|
||||
}
|
||||
|
||||
function return(x1, x2, x3, x4, y1, y2, y3, y4) {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
eth.finish(
|
||||
u256_to_i32ptr(x1, x2, x3, x4),
|
||||
u256_to_i32(y1, y2, y3, y4)
|
||||
)
|
||||
}
|
||||
function revert(x1, x2, x3, x4, y1, y2, y3, y4) {
|
||||
// TODO implement
|
||||
unreachable()
|
||||
eth.revert(
|
||||
u256_to_i32ptr(x1, x2, x3, x4),
|
||||
u256_to_i32(y1, y2, y3, y4)
|
||||
)
|
||||
}
|
||||
function invalid() {
|
||||
unreachable()
|
||||
|
@ -70,6 +70,13 @@ struct Continue { Label label; };
|
||||
|
||||
struct VariableDeclaration { std::string variableName; };
|
||||
struct GlobalVariableDeclaration { std::string variableName; };
|
||||
struct FunctionImport {
|
||||
std::string module;
|
||||
std::string externalName;
|
||||
std::string internalName;
|
||||
std::vector<std::string> paramTypes;
|
||||
std::unique_ptr<std::string> returnType;
|
||||
};
|
||||
|
||||
struct FunctionDefinition
|
||||
{
|
||||
|
@ -52,7 +52,14 @@ string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
|
||||
functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement)));
|
||||
}
|
||||
|
||||
return EWasmToText{}.run(transform.m_globalVariables, functions);
|
||||
std::vector<wasm::FunctionImport> imports;
|
||||
for (auto& imp: transform.m_functionsToImport)
|
||||
imports.emplace_back(std::move(imp.second));
|
||||
return EWasmToText{}.run(
|
||||
transform.m_globalVariables,
|
||||
imports,
|
||||
functions
|
||||
);
|
||||
}
|
||||
|
||||
wasm::Expression EWasmCodeTransform::generateMultiAssignment(
|
||||
@ -128,7 +135,25 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call)
|
||||
{
|
||||
if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name))
|
||||
{
|
||||
if (builtin->literalArguments)
|
||||
if (_call.functionName.name.str().substr(0, 4) == "eth.")
|
||||
{
|
||||
yulAssert(builtin->returns.size() <= 1, "");
|
||||
// Imported function, use regular call, but mark for import.
|
||||
if (!m_functionsToImport.count(builtin->name))
|
||||
{
|
||||
wasm::FunctionImport imp{
|
||||
"ethereum",
|
||||
builtin->name.str().substr(4),
|
||||
builtin->name.str(),
|
||||
{},
|
||||
builtin->returns.empty() ? nullptr : make_unique<string>(builtin->returns.front().str())
|
||||
};
|
||||
for (auto const& param: builtin->parameters)
|
||||
imp.paramTypes.emplace_back(param.str());
|
||||
m_functionsToImport[builtin->name] = std::move(imp);
|
||||
}
|
||||
}
|
||||
else if (builtin->literalArguments)
|
||||
{
|
||||
vector<wasm::Expression> literals;
|
||||
for (auto const& arg: _call.arguments)
|
||||
@ -138,12 +163,12 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call)
|
||||
else
|
||||
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)};
|
||||
|
||||
// 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)
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <libyul/optimiser/NameDispenser.h>
|
||||
|
||||
#include <stack>
|
||||
#include <map>
|
||||
|
||||
namespace yul
|
||||
{
|
||||
@ -90,6 +91,7 @@ private:
|
||||
|
||||
std::vector<wasm::VariableDeclaration> m_localVariables;
|
||||
std::vector<wasm::GlobalVariableDeclaration> m_globalVariables;
|
||||
std::map<YulString, wasm::FunctionImport> m_functionsToImport;
|
||||
std::stack<std::pair<std::string, std::string>> m_breakContinueLabelNames;
|
||||
};
|
||||
|
||||
|
@ -28,15 +28,24 @@
|
||||
|
||||
using namespace std;
|
||||
using namespace yul;
|
||||
using namespace dev;
|
||||
|
||||
string EWasmToText::run(
|
||||
vector<wasm::GlobalVariableDeclaration> const& _globals,
|
||||
vector<wasm::FunctionImport> const& _imports,
|
||||
vector<wasm::FunctionDefinition> const& _functions
|
||||
)
|
||||
{
|
||||
string ret = "(module\n";
|
||||
// TODO Add all the interface functions:
|
||||
// ret += " (import \"ethereum\" \"getBalance\" (func $getBalance (param i32 i32)))\n";
|
||||
for (wasm::FunctionImport const& imp: _imports)
|
||||
{
|
||||
ret += " (import \"" + imp.module + "\" \"" + imp.externalName + "\" (func $" + imp.internalName;
|
||||
if (!imp.paramTypes.empty())
|
||||
ret += " (param" + joinHumanReadablePrefixed(imp.paramTypes, " ", " ") + ")";
|
||||
if (imp.returnType)
|
||||
ret += " (result " + *imp.returnType + ")";
|
||||
ret += "))\n";
|
||||
}
|
||||
|
||||
// allocate one 64k page of memory and make it available to the Ethereum client
|
||||
ret += " (memory $memory (export \"memory\") 1)\n";
|
||||
@ -80,12 +89,14 @@ string EWasmToText::operator()(wasm::Label const& _label)
|
||||
|
||||
string EWasmToText::operator()(wasm::BuiltinCall const& _builtinCall)
|
||||
{
|
||||
return "(" + _builtinCall.functionName + " " + joinTransformed(_builtinCall.arguments) + ")";
|
||||
string args = joinTransformed(_builtinCall.arguments);
|
||||
return "(" + _builtinCall.functionName + (args.empty() ? "" : " " + args) + ")";
|
||||
}
|
||||
|
||||
string EWasmToText::operator()(wasm::FunctionCall const& _functionCall)
|
||||
{
|
||||
return "(call $" + _functionCall.functionName + " " + joinTransformed(_functionCall.arguments) + ")";
|
||||
string args = joinTransformed(_functionCall.arguments);
|
||||
return "(call $" + _functionCall.functionName + (args.empty() ? "" : " " + args) + ")";
|
||||
}
|
||||
|
||||
string EWasmToText::operator()(wasm::LocalAssignment const& _assignment)
|
||||
@ -100,16 +111,16 @@ string EWasmToText::operator()(wasm::GlobalAssignment const& _assignment)
|
||||
|
||||
string EWasmToText::operator()(wasm::If const& _if)
|
||||
{
|
||||
string text = "(if " + visit(*_if.condition) + " (then\n" + indented(joinTransformed(_if.statements)) + ")";
|
||||
string text = "(if " + visit(*_if.condition) + " (then\n" + indented(joinTransformed(_if.statements, '\n')) + ")";
|
||||
if (_if.elseStatements)
|
||||
text += "(else\n" + indented(joinTransformed(*_if.elseStatements)) + ")";
|
||||
text += "(else\n" + indented(joinTransformed(*_if.elseStatements, '\n')) + ")";
|
||||
return std::move(text) + ")\n";
|
||||
}
|
||||
|
||||
string EWasmToText::operator()(wasm::Loop const& _loop)
|
||||
{
|
||||
string label = _loop.labelName.empty() ? "" : " $" + _loop.labelName;
|
||||
return "(loop" + move(label) + "\n" + indented(joinTransformed(_loop.statements)) + ")\n";
|
||||
return "(loop" + move(label) + "\n" + indented(joinTransformed(_loop.statements, '\n')) + ")\n";
|
||||
}
|
||||
|
||||
string EWasmToText::operator()(wasm::Break const& _break)
|
||||
@ -125,17 +136,22 @@ string EWasmToText::operator()(wasm::Continue const& _continue)
|
||||
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";
|
||||
return "(block" + move(label) + "\n" + indented(joinTransformed(_block.statements, '\n')) + "\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);
|
||||
replacement.reserve(_in.size() + 4);
|
||||
replacement += " ";
|
||||
for (auto it = _in.begin(); it != _in.end(); ++it)
|
||||
if (*it == '\n' && it + 1 != _in.end() && *(it + 1) != '\n')
|
||||
replacement += "\n ";
|
||||
else
|
||||
replacement += *it;
|
||||
}
|
||||
return replacement;
|
||||
}
|
||||
@ -162,14 +178,14 @@ string EWasmToText::visit(wasm::Expression const& _expression)
|
||||
return boost::apply_visitor(*this, _expression);
|
||||
}
|
||||
|
||||
string EWasmToText::joinTransformed(vector<wasm::Expression> const& _expressions)
|
||||
string EWasmToText::joinTransformed(vector<wasm::Expression> const& _expressions, char _separator)
|
||||
{
|
||||
string ret;
|
||||
for (auto const& e: _expressions)
|
||||
{
|
||||
string t = visit(e);
|
||||
if (!t.empty() && !ret.empty() && ret.back() != '\n')
|
||||
ret += ' ';
|
||||
ret += _separator;
|
||||
ret += move(t);
|
||||
}
|
||||
return ret;
|
||||
|
@ -33,6 +33,7 @@ class EWasmToText: public boost::static_visitor<std::string>
|
||||
public:
|
||||
std::string run(
|
||||
std::vector<wasm::GlobalVariableDeclaration> const& _globals,
|
||||
std::vector<wasm::FunctionImport> const& _imports,
|
||||
std::vector<wasm::FunctionDefinition> const& _functions
|
||||
);
|
||||
|
||||
@ -58,7 +59,10 @@ private:
|
||||
std::string transform(wasm::FunctionDefinition const& _function);
|
||||
|
||||
std::string visit(wasm::Expression const& _expression);
|
||||
std::string joinTransformed(std::vector<wasm::Expression> const& _expressions);
|
||||
std::string joinTransformed(
|
||||
std::vector<wasm::Expression> const& _expressions,
|
||||
char _separator = ' '
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -65,6 +65,8 @@ WasmDialect::WasmDialect():
|
||||
|
||||
addFunction("datasize", 1, 4, true, true);
|
||||
addFunction("dataoffset", 1, 4, true, true);
|
||||
|
||||
addEthereumExternals();
|
||||
}
|
||||
|
||||
BuiltinFunction const* WasmDialect::builtin(YulString _name) const
|
||||
@ -85,6 +87,69 @@ WasmDialect const& WasmDialect::instance()
|
||||
return *dialect;
|
||||
}
|
||||
|
||||
void WasmDialect::addEthereumExternals()
|
||||
{
|
||||
// These are not YulStrings because that would be too complicated with regards
|
||||
// to the YulStringRepository reset.
|
||||
static string const i64{"i64"};
|
||||
static string const i32{"i32"};
|
||||
static string const i32ptr{"i32"}; // Uses "i32" on purpose.
|
||||
struct External { string name; vector<string> parameters; vector<string> returns; };
|
||||
static vector<External> externals{
|
||||
{"getAddress", {i32ptr}, {}},
|
||||
{"getExternalBalance", {i32ptr, i32ptr}, {}},
|
||||
{"getBlockHash", {i64, i32ptr}, {i32}},
|
||||
{"call", {i64, i32ptr, i32ptr, i32ptr, i32}, {i32}},
|
||||
{"callDataCopy", {i32ptr, i32, i32}, {}},
|
||||
{"getCallDataSize", {}, {i32}},
|
||||
{"callCode", {i64, i32ptr, i32ptr, i32ptr, i32}, {i32}},
|
||||
{"callDelegate", {i64, i32ptr, i32ptr, i32}, {i32}},
|
||||
{"callStatic", {i64, i32ptr, i32ptr, i32}, {i32}},
|
||||
{"storageStore", {i32ptr, i32ptr}, {}},
|
||||
{"storageLoad", {i32ptr, i32ptr}, {}},
|
||||
{"getCaller", {i32ptr}, {}},
|
||||
{"getCallValue", {i32ptr}, {}},
|
||||
{"codeCopy", {i32ptr, i32, i32}, {}},
|
||||
{"getCodeSize", {i32ptr}, {}},
|
||||
{"getBlockCoinbase", {i32ptr}, {}},
|
||||
{"create", {i32ptr, i32ptr, i32, i32ptr}, {i32}},
|
||||
{"getBlockDifficulty", {i32ptr}, {}},
|
||||
{"externalCodeCopy", {i32ptr, i32ptr, i32, i32}, {}},
|
||||
{"getExternalCodeSize", {i32ptr}, {i32}},
|
||||
{"getGasLeft", {}, {i64}},
|
||||
{"getBlockGasLimit", {}, {i64}},
|
||||
{"getTxGasPrice", {i32ptr}, {}},
|
||||
{"log", {i32ptr, i32, i32, i32ptr, i32ptr, i32ptr, i32ptr}, {}},
|
||||
{"getBlockNumber", {}, {i64}},
|
||||
{"getTxOrigin", {i32ptr}, {}},
|
||||
{"finish", {i32ptr, i32}, {}},
|
||||
{"revert", {i32ptr, i32}, {}},
|
||||
{"getReturnDataSize", {}, {i32}},
|
||||
{"returnDataCopy", {i32ptr, i32, i32}, {}},
|
||||
{"selfDestruct", {i32ptr}, {}},
|
||||
{"getBlockTimestamp", {}, {i64}}
|
||||
};
|
||||
for (External const& ext: externals)
|
||||
{
|
||||
YulString name{"eth." + ext.name};
|
||||
BuiltinFunction& f = m_functions[name];
|
||||
f.name = name;
|
||||
for (string const& p: ext.parameters)
|
||||
f.parameters.emplace_back(YulString(p));
|
||||
for (string const& p: ext.returns)
|
||||
f.returns.emplace_back(YulString(p));
|
||||
f.movable = false;
|
||||
// TODO some of them are side effect free.
|
||||
f.sideEffectFree = false;
|
||||
f.sideEffectFreeIfNoMSize = false;
|
||||
f.isMSize = false;
|
||||
f.invalidatesStorage = (ext.name == "storageStore");
|
||||
// TODO some of them do not invalidate memory
|
||||
f.invalidatesMemory = true;
|
||||
f.literalArguments = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WasmDialect::addFunction(
|
||||
string _name,
|
||||
size_t _params,
|
||||
|
@ -53,6 +53,8 @@ struct WasmDialect: public Dialect
|
||||
static WasmDialect const& instance();
|
||||
|
||||
private:
|
||||
void addEthereumExternals();
|
||||
|
||||
void addFunction(std::string _name, size_t _params, size_t _returns, bool _movable = true, bool _literalArguments = false);
|
||||
|
||||
std::map<YulString, BuiltinFunction> m_functions;
|
||||
|
@ -1,18 +1,160 @@
|
||||
{"contracts":{"A":{"C":{"ewasm":{"wast":"(module
|
||||
(import \"ethereum\" \"revert\" (func $eth.revert (param i32 i32)))
|
||||
(memory $memory (export \"memory\") 1)
|
||||
(export \"main\" (func $main))
|
||||
|
||||
(func $main
|
||||
(unreachable ) (unreachable )
|
||||
(local $_1 i64)
|
||||
(local $pos i64)
|
||||
(local $_2 i64)
|
||||
(local $_3 i64)
|
||||
(local $hi i64)
|
||||
(local $_4 i64)
|
||||
(local $hi_1 i64)
|
||||
(local $hi_2 i64)
|
||||
(local $hi_3 i64)
|
||||
(local $hi_4 i64)
|
||||
(set_local $_1 (i64.const 0))
|
||||
(set_local $pos (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (i64.const 64)))
|
||||
(set_local $_2 (i64.const 32))
|
||||
(set_local $_3 (i64.shr_u (get_local $_1) (i64.const 16)))
|
||||
(set_local $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (get_local $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (get_local $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (get_local $_3))) (get_local $_2)))
|
||||
(set_local $_4 (i64.shr_u (get_local $_1) (get_local $_2)))
|
||||
(i64.store (get_local $pos) (i64.or (get_local $hi) (call $endian_swap_32 (get_local $_4)))) (set_local $hi_1 (i64.shl (call $endian_swap_16 (get_local $_1)) (i64.const 16)))
|
||||
(set_local $hi_2 (i64.shl (i64.or (get_local $hi_1) (call $endian_swap_16 (get_local $_3))) (get_local $_2)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 8)) (i64.or (get_local $hi_2) (call $endian_swap_32 (get_local $_4)))) (set_local $hi_3 (i64.shl (call $endian_swap_32 (get_local $_1)) (get_local $_2)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 16)) (i64.or (get_local $hi_3) (call $endian_swap_32 (get_local $_4)))) (set_local $hi_4 (i64.shl (call $endian_swap_32 (i64.const 64)) (get_local $_2)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 24)) (i64.or (get_local $hi_4) (call $endian_swap_32 (i64.shr_u (i64.const 64) (get_local $_2))))) (call $eth.revert (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_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.const 0) (i64.or (i64.or (get_local $x1) (get_local $x2)) (get_local $x3))) (then
|
||||
(unreachable)))
|
||||
(if (i64.ne (i64.const 0) (i64.shr_u (get_local $x4) (i64.const 32))) (then
|
||||
(unreachable)))
|
||||
(set_local $v (get_local $x4))
|
||||
(get_local $v)
|
||||
)
|
||||
|
||||
(func $endian_swap_16
|
||||
(param $x i64)
|
||||
(result i64)
|
||||
(local $y i64)
|
||||
(set_local $y (i64.or (i64.and (i64.shl (get_local $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (get_local $x) (i64.const 8)) (i64.const 255))))
|
||||
(get_local $y)
|
||||
)
|
||||
|
||||
(func $endian_swap_32
|
||||
(param $x i64)
|
||||
(result i64)
|
||||
(local $y i64)
|
||||
(local $hi i64)
|
||||
(set_local $hi (i64.shl (call $endian_swap_16 (get_local $x)) (i64.const 16)))
|
||||
(set_local $y (i64.or (get_local $hi) (call $endian_swap_16 (i64.shr_u (get_local $x) (i64.const 16)))))
|
||||
(get_local $y)
|
||||
)
|
||||
|
||||
)
|
||||
(module
|
||||
(import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32)))
|
||||
(import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32)))
|
||||
(memory $memory (export \"memory\") 1)
|
||||
(export \"main\" (func $main))
|
||||
(global $global_ (mut i64) (i64.const 0))
|
||||
(global $global__1 (mut i64) (i64.const 0))
|
||||
(global $global__2 (mut i64) (i64.const 0))
|
||||
|
||||
(func $main
|
||||
(unreachable ) (unreachable ) (unreachable )
|
||||
(local $_1 i64)
|
||||
(local $pos i64)
|
||||
(local $hi i64)
|
||||
(local $_2 i64)
|
||||
(local $hi_1 i64)
|
||||
(local $_3 i64)
|
||||
(local $hi_2 i64)
|
||||
(local $hi_3 i64)
|
||||
(local $hi_4 i64)
|
||||
(local $_4 i64)
|
||||
(local $_5 i64)
|
||||
(local $_6 i64)
|
||||
(local $_7 i64)
|
||||
(local $_8 i64)
|
||||
(local $_9 i64)
|
||||
(local $_10 i64)
|
||||
(local $_11 i64)
|
||||
(set_local $_1 (i64.const 0))
|
||||
(set_local $pos (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (i64.const 64)))
|
||||
(set_local $hi (i64.shl (call $endian_swap_16 (get_local $_1)) (i64.const 16)))
|
||||
(set_local $_2 (i64.shr_u (get_local $_1) (i64.const 16)))
|
||||
(set_local $hi_1 (i64.shl (i64.or (get_local $hi) (call $endian_swap_16 (get_local $_2))) (i64.const 32)))
|
||||
(set_local $_3 (i64.shr_u (get_local $_1) (i64.const 32)))
|
||||
(i64.store (get_local $pos) (i64.or (get_local $hi_1) (call $endian_swap_32 (get_local $_3)))) (set_local $hi_2 (i64.shl (call $endian_swap_16 (get_local $_1)) (i64.const 16)))
|
||||
(set_local $hi_3 (i64.shl (i64.or (get_local $hi_2) (call $endian_swap_16 (get_local $_2))) (i64.const 32)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 8)) (i64.or (get_local $hi_3) (call $endian_swap_32 (get_local $_3)))) (set_local $hi_4 (i64.shl (call $endian_swap_32 (get_local $_1)) (i64.const 32)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 16)) (i64.or (get_local $hi_4) (call $endian_swap_32 (get_local $_3)))) (i64.store (i64.add (get_local $pos) (i64.const 24)) (call $endian_swap (i64.const 64))) (block
|
||||
(set_local $_4 (datasize \"C_2_deployed\"))
|
||||
(set_local $_5 (get_global $global_))
|
||||
(set_local $_6 (get_global $global__1))
|
||||
(set_local $_7 (get_global $global__2))
|
||||
|
||||
)
|
||||
(block
|
||||
(set_local $_8 (dataoffset \"C_2_deployed\"))
|
||||
(set_local $_9 (get_global $global_))
|
||||
(set_local $_10 (get_global $global__1))
|
||||
(set_local $_11 (get_global $global__2))
|
||||
|
||||
)
|
||||
(call $eth.codeCopy (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_8) (get_local $_9) (get_local $_10) (get_local $_11)) (call $u256_to_i32 (get_local $_4) (get_local $_5) (get_local $_6) (get_local $_7))) (call $eth.finish (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_4) (get_local $_5) (get_local $_6) (get_local $_7)))
|
||||
)
|
||||
|
||||
(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.const 0) (i64.or (i64.or (get_local $x1) (get_local $x2)) (get_local $x3))) (then
|
||||
(unreachable)))
|
||||
(if (i64.ne (i64.const 0) (i64.shr_u (get_local $x4) (i64.const 32))) (then
|
||||
(unreachable)))
|
||||
(set_local $v (get_local $x4))
|
||||
(get_local $v)
|
||||
)
|
||||
|
||||
(func $endian_swap_16
|
||||
(param $x i64)
|
||||
(result i64)
|
||||
(local $y i64)
|
||||
(set_local $y (i64.or (i64.and (i64.shl (get_local $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (get_local $x) (i64.const 8)) (i64.const 255))))
|
||||
(get_local $y)
|
||||
)
|
||||
|
||||
(func $endian_swap_32
|
||||
(param $x i64)
|
||||
(result i64)
|
||||
(local $y i64)
|
||||
(local $hi i64)
|
||||
(set_local $hi (i64.shl (call $endian_swap_16 (get_local $x)) (i64.const 16)))
|
||||
(set_local $y (i64.or (get_local $hi) (call $endian_swap_16 (i64.shr_u (get_local $x) (i64.const 16)))))
|
||||
(get_local $y)
|
||||
)
|
||||
|
||||
(func $endian_swap
|
||||
(param $x i64)
|
||||
(result i64)
|
||||
(local $y i64)
|
||||
(local $hi i64)
|
||||
(set_local $hi (i64.shl (call $endian_swap_32 (get_local $x)) (i64.const 32)))
|
||||
(set_local $y (i64.or (get_local $hi) (call $endian_swap_32 (i64.shr_u (get_local $x) (i64.const 32)))))
|
||||
(get_local $y)
|
||||
)
|
||||
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user