diff --git a/libyul/backends/wasm/EVMToEWasmTranslator.cpp b/libyul/backends/wasm/EVMToEWasmTranslator.cpp index 0f5d7eab1..dfcd5967a 100644 --- a/libyul/backends/wasm/EVMToEWasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEWasmTranslator.cpp @@ -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() diff --git a/libyul/backends/wasm/EWasmAST.h b/libyul/backends/wasm/EWasmAST.h index 39172db36..e3ce6604f 100644 --- a/libyul/backends/wasm/EWasmAST.h +++ b/libyul/backends/wasm/EWasmAST.h @@ -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 paramTypes; + std::unique_ptr returnType; +}; struct FunctionDefinition { diff --git a/libyul/backends/wasm/EWasmCodeTransform.cpp b/libyul/backends/wasm/EWasmCodeTransform.cpp index e345c4946..3bcc18fe8 100644 --- a/libyul/backends/wasm/EWasmCodeTransform.cpp +++ b/libyul/backends/wasm/EWasmCodeTransform.cpp @@ -52,7 +52,14 @@ string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) functions.emplace_back(transform.translateFunction(boost::get(statement))); } - return EWasmToText{}.run(transform.m_globalVariables, functions); + std::vector 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(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 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) diff --git a/libyul/backends/wasm/EWasmCodeTransform.h b/libyul/backends/wasm/EWasmCodeTransform.h index 399428a31..bbe0e1b69 100644 --- a/libyul/backends/wasm/EWasmCodeTransform.h +++ b/libyul/backends/wasm/EWasmCodeTransform.h @@ -26,6 +26,7 @@ #include #include +#include namespace yul { @@ -90,6 +91,7 @@ private: std::vector m_localVariables; std::vector m_globalVariables; + std::map m_functionsToImport; std::stack> m_breakContinueLabelNames; }; diff --git a/libyul/backends/wasm/EWasmToText.cpp b/libyul/backends/wasm/EWasmToText.cpp index ec6060e71..ac49247fa 100644 --- a/libyul/backends/wasm/EWasmToText.cpp +++ b/libyul/backends/wasm/EWasmToText.cpp @@ -28,15 +28,24 @@ using namespace std; using namespace yul; +using namespace dev; string EWasmToText::run( vector const& _globals, + vector const& _imports, vector 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"; diff --git a/libyul/backends/wasm/EWasmToText.h b/libyul/backends/wasm/EWasmToText.h index 91c64daba..b8bafc0cd 100644 --- a/libyul/backends/wasm/EWasmToText.h +++ b/libyul/backends/wasm/EWasmToText.h @@ -33,6 +33,7 @@ class EWasmToText: public boost::static_visitor public: std::string run( std::vector const& _globals, + std::vector const& _imports, std::vector const& _functions ); diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index cd4c2ecd8..d06fe2edd 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -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 parameters; vector returns; }; + static vector 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, diff --git a/libyul/backends/wasm/WasmDialect.h b/libyul/backends/wasm/WasmDialect.h index 8613889e2..45bea3bca 100644 --- a/libyul/backends/wasm/WasmDialect.h +++ b/libyul/backends/wasm/WasmDialect.h @@ -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 m_functions; diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_eWasm_requested/output.json index ee3ae6195..2707bc702 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_eWasm_requested/output.json @@ -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) ) )