Add eWasm externals.

This commit is contained in:
chriseth 2019-07-09 15:19:50 +02:00
parent f3bdc79187
commit e3433aa4eb
9 changed files with 418 additions and 64 deletions

View File

@ -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()

View File

@ -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
{

View File

@ -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,7 +163,7 @@ 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,

View File

@ -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;
};

View File

@ -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";

View File

@ -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
);

View File

@ -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,

View File

@ -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;

View File

@ -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)
)
)