summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/wasm/wasm-emscripten.cpp184
-rw-r--r--test/lld/em_asm_main_thread.wast196
-rw-r--r--test/lld/em_asm_main_thread.wast.out270
3 files changed, 579 insertions, 71 deletions
diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp
index 9b579fa25..6710c7cd8 100644
--- a/src/wasm/wasm-emscripten.cpp
+++ b/src/wasm/wasm-emscripten.cpp
@@ -31,7 +31,7 @@
namespace wasm {
-cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const");
+cashew::IString EM_ASM_PREFIX("emscripten_asm_const");
cashew::IString EM_JS_PREFIX("__em_js__");
static Name STACK_SAVE("stackSave");
@@ -664,13 +664,36 @@ std::string codeForConstAddr(Module& wasm,
return escape(str);
}
+enum class Proxying {
+ None,
+ Sync,
+ Async,
+};
+
+std::string proxyingSuffix(Proxying proxy) {
+ switch (proxy) {
+ case Proxying::None:
+ return "";
+ case Proxying::Sync:
+ return "sync_on_main_thread_";
+ case Proxying::Async:
+ return "async_on_main_thread_";
+ }
+ WASM_UNREACHABLE();
+}
+
struct AsmConstWalker : public LinearExecutionWalker<AsmConstWalker> {
Module& wasm;
std::vector<Address> segmentOffsets; // segment index => address offset
- std::map<std::string, std::set<std::string>> sigsForCode;
- std::map<std::string, Address> ids;
- std::set<std::string> allSigs;
+ struct AsmConst {
+ std::set<std::string> sigs;
+ Address id;
+ Proxying proxy;
+ };
+
+ std::map<std::string, AsmConst> asmConsts;
+ std::set<std::pair<std::string, Proxying>> allSigs;
// last sets in the current basic block, per index
std::map<Index, LocalSet*> sets;
@@ -686,12 +709,13 @@ struct AsmConstWalker : public LinearExecutionWalker<AsmConstWalker> {
void process();
private:
- std::string fixupNameWithSig(Name& name, std::string baseSig);
- Literal idLiteralForCode(std::string code);
+ std::string fixupName(Name& name, std::string baseSig, Proxying proxy);
+ AsmConst& createAsmConst(std::string code, std::string sig, Name name);
std::string asmConstSig(std::string baseSig);
- Name nameForImportWithSig(std::string sig);
+ Name nameForImportWithSig(std::string sig, Proxying proxy);
void queueImport(Name importName, std::string baseSig);
void addImports();
+ Proxying proxyType(Name name);
std::vector<std::unique_ptr<Function>> queuedImports;
};
@@ -707,57 +731,74 @@ void AsmConstWalker::visitCall(Call* curr) {
auto* import = wasm.getFunction(curr->target);
// Find calls to emscripten_asm_const* functions whose first argument is
// is always a string constant.
- if (import->imported() && import->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
- auto baseSig = getSig(curr);
- auto sig = fixupNameWithSig(curr->target, baseSig);
- auto* arg = curr->operands[0];
- while (!arg->dynCast<Const>()) {
- if (auto* get = arg->dynCast<LocalGet>()) {
- // The argument may be a local.get, in which case, the last set in this
- // basic block has the value.
- auto* set = sets[get->index];
- if (set) {
- assert(set->index == get->index);
- arg = set->value;
- } else {
- Fatal() << "local.get of unknown in arg0 of call to " << import->base
- << " (used by EM_ASM* macros) in function "
- << getFunction()->name
- << ".\nThis might be caused by aggressive compiler "
- "transformations. Consider using EM_JS instead.";
- }
- } else if (auto* value = arg->dynCast<Binary>()) {
- // In the dynamic linking case the address of the string constant
- // is the result of adding its offset to __memory_base.
- // In this case are only looking for the offset with the data segment so
- // the RHS of the addition is just what we want.
- assert(value->op == AddInt32);
- arg = value->right;
+ if (!import->imported()) {
+ return;
+ }
+ auto importName = import->base;
+ if (!importName.hasSubstring(EM_ASM_PREFIX)) {
+ return;
+ }
+
+ auto baseSig = getSig(curr);
+ auto sig = asmConstSig(baseSig);
+ auto* arg = curr->operands[0];
+ while (!arg->dynCast<Const>()) {
+ if (auto* get = arg->dynCast<LocalGet>()) {
+ // The argument may be a local.get, in which case, the last set in this
+ // basic block has the value.
+ auto* set = sets[get->index];
+ if (set) {
+ assert(set->index == get->index);
+ arg = set->value;
} else {
- if (!value) {
- Fatal() << "Unexpected arg0 type (" << getExpressionName(arg)
- << ") in call to: " << import->base;
- }
+ Fatal() << "local.get of unknown in arg0 of call to " << importName
+ << " (used by EM_ASM* macros) in function "
+ << getFunction()->name
+ << ".\nThis might be caused by aggressive compiler "
+ "transformations. Consider using EM_JS instead.";
+ }
+ } else if (auto* value = arg->dynCast<Binary>()) {
+ // In the dynamic linking case the address of the string constant
+ // is the result of adding its offset to __memory_base.
+ // In this case are only looking for the offset with the data segment so
+ // the RHS of the addition is just what we want.
+ assert(value->op == AddInt32);
+ arg = value->right;
+ } else {
+ if (!value) {
+ Fatal() << "Unexpected arg0 type (" << getExpressionName(arg)
+ << ") in call to: " << importName;
}
}
+ }
- auto* value = arg->cast<Const>();
- auto code = codeForConstAddr(wasm, segmentOffsets, value);
- sigsForCode[code].insert(sig);
+ auto* value = arg->cast<Const>();
+ auto code = codeForConstAddr(wasm, segmentOffsets, value);
+ auto& asmConst = createAsmConst(code, sig, importName);
+ fixupName(curr->target, baseSig, asmConst.proxy);
- // Replace the first argument to the call with a Const index
- Builder builder(wasm);
- curr->operands[0] = builder.makeConst(idLiteralForCode(code));
+ // Replace the first argument to the call with a Const index
+ Builder builder(wasm);
+ curr->operands[0] = builder.makeConst(Literal(asmConst.id));
+}
+
+Proxying AsmConstWalker::proxyType(Name name) {
+ if (name.hasSubstring("_sync_on_main_thread")) {
+ return Proxying::Sync;
+ } else if (name.hasSubstring("_async_on_main_thread")) {
+ return Proxying::Async;
}
+ return Proxying::None;
}
void AsmConstWalker::visitTable(Table* curr) {
for (auto& segment : curr->segments) {
for (auto& name : segment.data) {
auto* func = wasm.getFunction(name);
- if (func->imported() && func->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
+ if (func->imported() && func->base.hasSubstring(EM_ASM_PREFIX)) {
std::string baseSig = getSig(func);
- fixupNameWithSig(name, baseSig);
+ auto proxy = proxyType(func->base);
+ fixupName(name, baseSig, proxy);
}
}
}
@@ -771,27 +812,31 @@ void AsmConstWalker::process() {
addImports();
}
-std::string AsmConstWalker::fixupNameWithSig(Name& name, std::string baseSig) {
+std::string
+AsmConstWalker::fixupName(Name& name, std::string baseSig, Proxying proxy) {
auto sig = asmConstSig(baseSig);
- auto importName = nameForImportWithSig(sig);
+ auto importName = nameForImportWithSig(sig, proxy);
name = importName;
- if (allSigs.count(sig) == 0) {
- allSigs.insert(sig);
+ auto pair = std::make_pair(sig, proxy);
+ if (allSigs.count(pair) == 0) {
+ allSigs.insert(pair);
queueImport(importName, baseSig);
}
return sig;
}
-Literal AsmConstWalker::idLiteralForCode(std::string code) {
- int32_t id;
- if (ids.count(code) == 0) {
- id = ids.size();
- ids[code] = id;
- } else {
- id = ids[code];
+AsmConstWalker::AsmConst&
+AsmConstWalker::createAsmConst(std::string code, std::string sig, Name name) {
+ if (asmConsts.count(code) == 0) {
+ AsmConst asmConst;
+ asmConst.id = asmConsts.size();
+ asmConst.sigs.insert(sig);
+ asmConst.proxy = proxyType(name);
+
+ asmConsts[code] = asmConst;
}
- return Literal(id);
+ return asmConsts[code];
}
std::string AsmConstWalker::asmConstSig(std::string baseSig) {
@@ -806,8 +851,9 @@ std::string AsmConstWalker::asmConstSig(std::string baseSig) {
return sig;
}
-Name AsmConstWalker::nameForImportWithSig(std::string sig) {
- std::string fixedTarget = EMSCRIPTEN_ASM_CONST.str + std::string("_") + sig;
+Name AsmConstWalker::nameForImportWithSig(std::string sig, Proxying proxy) {
+ std::string fixedTarget =
+ EM_ASM_PREFIX.str + std::string("_") + proxyingSuffix(proxy) + sig;
return Name(fixedTarget.c_str());
}
@@ -830,7 +876,7 @@ AsmConstWalker fixEmAsmConstsAndReturnWalker(Module& wasm) {
// This would find our generated functions if we ran it later
std::vector<Name> toRemove;
for (auto& import : wasm.functions) {
- if (import->imported() && import->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
+ if (import->imported() && import->base.hasSubstring(EM_ASM_PREFIX)) {
toRemove.push_back(import->name);
}
}
@@ -1039,19 +1085,15 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
// print
commaFirst = true;
- if (!emAsmWalker.sigsForCode.empty()) {
+ if (!emAsmWalker.asmConsts.empty()) {
meta << " \"asmConsts\": {";
- for (auto& pair : emAsmWalker.sigsForCode) {
+ for (auto& pair : emAsmWalker.asmConsts) {
auto& code = pair.first;
- auto& sigs = pair.second;
+ auto& asmConst = pair.second;
meta << nextElement();
- meta << '"' << emAsmWalker.ids[code] << "\": [\"" << code << "\", ";
- printSet(meta, sigs);
- meta << ", ";
-
- // TODO: proxying to main thread. Currently this is unsupported, so proxy
- // mode is "none", represented by an empty string.
- meta << "[\"\"]";
+ meta << '"' << asmConst.id << "\": [\"" << code << "\", ";
+ printSet(meta, asmConst.sigs);
+ meta << ", [\"" << proxyingSuffix(asmConst.proxy) << "\"]";
meta << "]";
}
@@ -1097,7 +1139,7 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
commaFirst = true;
ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) {
if (emJsWalker.codeByName.count(import->base.str) == 0 &&
- !import->base.startsWith(EMSCRIPTEN_ASM_CONST.str) &&
+ !import->base.startsWith(EM_ASM_PREFIX.str) &&
!import->base.startsWith("invoke_")) {
if (declares.insert(import->base.str).second) {
meta << nextElement() << '"' << import->base.str << '"';
diff --git a/test/lld/em_asm_main_thread.wast b/test/lld/em_asm_main_thread.wast
new file mode 100644
index 000000000..93735c7a9
--- /dev/null
+++ b/test/lld/em_asm_main_thread.wast
@@ -0,0 +1,196 @@
+(; Copied from em_asm.wast
+ s/emscripten_asm_const_int/emscripten_asm_const_int_sync_on_main_thread/g
+;)
+(module
+ (type $0 (func (param i32 i32 i32) (result i32)))
+ (type $1 (func))
+ (type $2 (func (result i32)))
+ (type $3 (func (param i32 i32 i32)))
+ (type $4 (func (param i32 i32)))
+ (type $5 (func (param i32) (result i32)))
+ (type $6 (func (param i32 i32) (result i32)))
+ (import "env" "emscripten_asm_const_int_sync_on_main_thread" (func $emscripten_asm_const_int_sync_on_main_thread (param i32 i32 i32) (result i32)))
+ (memory $0 2)
+ (data (i32.const 568) "{ Module.print(\"Hello world\"); }\00{ return $0 + $1; }\00{ Module.print(\"Got \" + $0); }\00")
+ (table $0 1 1 funcref)
+ (global $global$0 (mut i32) (i32.const 66192))
+ (global $global$1 i32 (i32.const 66192))
+ (global $global$2 i32 (i32.const 652))
+ (export "memory" (memory $0))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (export "__data_end" (global $global$2))
+ (export "main" (func $main))
+ (func $__wasm_call_ctors (; 1 ;) (type $1)
+ )
+ (func $__original_main (; 2 ;) (type $2) (result i32)
+ (local $0 i32)
+ (local $1 i32)
+ (global.set $global$0
+ (local.tee $0
+ (i32.sub
+ (global.get $global$0)
+ (i32.const 32)
+ )
+ )
+ )
+ (i32.store8 offset=24
+ (local.get $0)
+ (call $__em_asm_sig_builder::inner<>\20const\20__em_asm_sig_builder::__em_asm_sig<>\28\29)
+ )
+ (drop
+ (call $emscripten_asm_const_int_sync_on_main_thread
+ (i32.const 568)
+ (i32.add
+ (local.get $0)
+ (i32.const 24)
+ )
+ (i32.const 0)
+ )
+ )
+ (call $__em_asm_sig_builder::inner<int\2c\20int>\20const\20__em_asm_sig_builder::__em_asm_sig<int\2c\20int>\28int\2c\20int\29
+ (i32.add
+ (local.get $0)
+ (i32.const 24)
+ )
+ (i32.const 13)
+ (i32.const 27)
+ )
+ (i64.store offset=16
+ (local.get $0)
+ (i64.const 115964117005)
+ )
+ (call $__em_asm_sig_builder::inner<int>\20const\20__em_asm_sig_builder::__em_asm_sig<int>\28int\29
+ (i32.add
+ (local.get $0)
+ (i32.const 24)
+ )
+ (local.tee $1
+ (call $emscripten_asm_const_int_sync_on_main_thread
+ (i32.const 601)
+ (i32.add
+ (local.get $0)
+ (i32.const 24)
+ )
+ (i32.add
+ (local.get $0)
+ (i32.const 16)
+ )
+ )
+ )
+ )
+ (i32.store
+ (local.get $0)
+ (local.get $1)
+ )
+ (drop
+ (call $emscripten_asm_const_int_sync_on_main_thread
+ (i32.const 621)
+ (i32.add
+ (local.get $0)
+ (i32.const 24)
+ )
+ (local.get $0)
+ )
+ )
+ (global.set $global$0
+ (i32.add
+ (local.get $0)
+ (i32.const 32)
+ )
+ )
+ (i32.const 0)
+ )
+ (func $__em_asm_sig_builder::inner<>\20const\20__em_asm_sig_builder::__em_asm_sig<>\28\29 (; 3 ;) (type $2) (result i32)
+ (i32.const 0)
+ )
+ (func $__em_asm_sig_builder::inner<int\2c\20int>\20const\20__em_asm_sig_builder::__em_asm_sig<int\2c\20int>\28int\2c\20int\29 (; 4 ;) (type $3) (param $0 i32) (param $1 i32) (param $2 i32)
+ (local $3 i32)
+ (global.set $global$0
+ (local.tee $3
+ (i32.sub
+ (global.get $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (i32.store8 offset=13
+ (local.get $3)
+ (call $__em_asm_sig_builder::sig_char\28int\29
+ (local.get $1)
+ )
+ )
+ (local.set $2
+ (call $__em_asm_sig_builder::sig_char\28int\29
+ (local.get $2)
+ )
+ )
+ (i32.store8
+ (i32.add
+ (local.get $0)
+ (i32.const 2)
+ )
+ (i32.const 0)
+ )
+ (i32.store8 offset=14
+ (local.get $3)
+ (local.get $2)
+ )
+ (i32.store16 align=1
+ (local.get $0)
+ (i32.load16_u offset=13 align=1
+ (local.get $3)
+ )
+ )
+ (global.set $global$0
+ (i32.add
+ (local.get $3)
+ (i32.const 16)
+ )
+ )
+ )
+ (func $__em_asm_sig_builder::inner<int>\20const\20__em_asm_sig_builder::__em_asm_sig<int>\28int\29 (; 5 ;) (type $4) (param $0 i32) (param $1 i32)
+ (local $2 i32)
+ (global.set $global$0
+ (local.tee $2
+ (i32.sub
+ (global.get $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (local.set $1
+ (call $__em_asm_sig_builder::sig_char\28int\29
+ (local.get $1)
+ )
+ )
+ (i32.store8 offset=15
+ (local.get $2)
+ (i32.const 0)
+ )
+ (i32.store8 offset=14
+ (local.get $2)
+ (local.get $1)
+ )
+ (i32.store16 align=1
+ (local.get $0)
+ (i32.load16_u offset=14
+ (local.get $2)
+ )
+ )
+ (global.set $global$0
+ (i32.add
+ (local.get $2)
+ (i32.const 16)
+ )
+ )
+ )
+ (func $__em_asm_sig_builder::sig_char\28int\29 (; 6 ;) (type $5) (param $0 i32) (result i32)
+ (i32.const 105)
+ )
+ (func $main (; 7 ;) (type $6) (param $0 i32) (param $1 i32) (result i32)
+ (call $__original_main)
+ )
+ ;; custom section "producers", size 111
+)
+
diff --git a/test/lld/em_asm_main_thread.wast.out b/test/lld/em_asm_main_thread.wast.out
new file mode 100644
index 000000000..aac85da82
--- /dev/null
+++ b/test/lld/em_asm_main_thread.wast.out
@@ -0,0 +1,270 @@
+(module
+ (type $0 (func (param i32 i32 i32) (result i32)))
+ (type $1 (func))
+ (type $2 (func (result i32)))
+ (type $3 (func (param i32 i32 i32)))
+ (type $4 (func (param i32 i32)))
+ (type $5 (func (param i32) (result i32)))
+ (type $6 (func (param i32 i32) (result i32)))
+ (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32)))
+ (import "env" "emscripten_asm_const_sync_on_main_thread_iii" (func $emscripten_asm_const_sync_on_main_thread_iii (param i32 i32 i32) (result i32)))
+ (memory $0 2)
+ (data (i32.const 568) "{ Module.print(\"Hello world\"); }\00{ return $0 + $1; }\00{ Module.print(\"Got \" + $0); }\00")
+ (table $0 1 1 funcref)
+ (global $global$0 (mut i32) (i32.const 66192))
+ (global $global$1 i32 (i32.const 66192))
+ (global $global$2 i32 (i32.const 652))
+ (export "memory" (memory $0))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (export "__data_end" (global $global$2))
+ (export "main" (func $main))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (func $__wasm_call_ctors (; 1 ;) (type $1)
+ (nop)
+ )
+ (func $__original_main (; 2 ;) (type $2) (result i32)
+ (local $0 i32)
+ (local $1 i32)
+ (global.set $global$0
+ (local.tee $0
+ (i32.sub
+ (global.get $global$0)
+ (i32.const 32)
+ )
+ )
+ )
+ (i32.store8 offset=24
+ (local.get $0)
+ (call $__em_asm_sig_builder::inner<>\20const\20__em_asm_sig_builder::__em_asm_sig<>\28\29)
+ )
+ (drop
+ (call $emscripten_asm_const_sync_on_main_thread_iii
+ (i32.const 0)
+ (i32.add
+ (local.get $0)
+ (i32.const 24)
+ )
+ (i32.const 0)
+ )
+ )
+ (call $__em_asm_sig_builder::inner<int\2c\20int>\20const\20__em_asm_sig_builder::__em_asm_sig<int\2c\20int>\28int\2c\20int\29
+ (i32.add
+ (local.get $0)
+ (i32.const 24)
+ )
+ (i32.const 13)
+ (i32.const 27)
+ )
+ (i64.store offset=16
+ (local.get $0)
+ (i64.const 115964117005)
+ )
+ (call $__em_asm_sig_builder::inner<int>\20const\20__em_asm_sig_builder::__em_asm_sig<int>\28int\29
+ (i32.add
+ (local.get $0)
+ (i32.const 24)
+ )
+ (local.tee $1
+ (call $emscripten_asm_const_sync_on_main_thread_iii
+ (i32.const 1)
+ (i32.add
+ (local.get $0)
+ (i32.const 24)
+ )
+ (i32.add
+ (local.get $0)
+ (i32.const 16)
+ )
+ )
+ )
+ )
+ (i32.store
+ (local.get $0)
+ (local.get $1)
+ )
+ (drop
+ (call $emscripten_asm_const_sync_on_main_thread_iii
+ (i32.const 2)
+ (i32.add
+ (local.get $0)
+ (i32.const 24)
+ )
+ (local.get $0)
+ )
+ )
+ (global.set $global$0
+ (i32.add
+ (local.get $0)
+ (i32.const 32)
+ )
+ )
+ (i32.const 0)
+ )
+ (func $__em_asm_sig_builder::inner<>\20const\20__em_asm_sig_builder::__em_asm_sig<>\28\29 (; 3 ;) (type $2) (result i32)
+ (i32.const 0)
+ )
+ (func $__em_asm_sig_builder::inner<int\2c\20int>\20const\20__em_asm_sig_builder::__em_asm_sig<int\2c\20int>\28int\2c\20int\29 (; 4 ;) (type $3) (param $0 i32) (param $1 i32) (param $2 i32)
+ (local $3 i32)
+ (global.set $global$0
+ (local.tee $3
+ (i32.sub
+ (global.get $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (i32.store8 offset=13
+ (local.get $3)
+ (call $__em_asm_sig_builder::sig_char\28int\29
+ (local.get $1)
+ )
+ )
+ (local.set $2
+ (call $__em_asm_sig_builder::sig_char\28int\29
+ (local.get $2)
+ )
+ )
+ (i32.store8
+ (i32.add
+ (local.get $0)
+ (i32.const 2)
+ )
+ (i32.const 0)
+ )
+ (i32.store8 offset=14
+ (local.get $3)
+ (local.get $2)
+ )
+ (i32.store16 align=1
+ (local.get $0)
+ (i32.load16_u offset=13 align=1
+ (local.get $3)
+ )
+ )
+ (global.set $global$0
+ (i32.add
+ (local.get $3)
+ (i32.const 16)
+ )
+ )
+ )
+ (func $__em_asm_sig_builder::inner<int>\20const\20__em_asm_sig_builder::__em_asm_sig<int>\28int\29 (; 5 ;) (type $4) (param $0 i32) (param $1 i32)
+ (local $2 i32)
+ (global.set $global$0
+ (local.tee $2
+ (i32.sub
+ (global.get $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (local.set $1
+ (call $__em_asm_sig_builder::sig_char\28int\29
+ (local.get $1)
+ )
+ )
+ (i32.store8 offset=15
+ (local.get $2)
+ (i32.const 0)
+ )
+ (i32.store8 offset=14
+ (local.get $2)
+ (local.get $1)
+ )
+ (i32.store16 align=1
+ (local.get $0)
+ (i32.load16_u offset=14
+ (local.get $2)
+ )
+ )
+ (global.set $global$0
+ (i32.add
+ (local.get $2)
+ (i32.const 16)
+ )
+ )
+ )
+ (func $__em_asm_sig_builder::sig_char\28int\29 (; 6 ;) (type $5) (param $0 i32) (result i32)
+ (i32.const 105)
+ )
+ (func $main (; 7 ;) (type $6) (param $0 i32) (param $1 i32) (result i32)
+ (call $__original_main)
+ )
+ (func $stackSave (; 8 ;) (result i32)
+ (global.get $global$0)
+ )
+ (func $stackAlloc (; 9 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (global.set $global$0
+ (local.tee $1
+ (i32.and
+ (i32.sub
+ (global.get $global$0)
+ (local.get $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (local.get $1)
+ )
+ (func $stackRestore (; 10 ;) (param $0 i32)
+ (global.set $global$0
+ (local.get $0)
+ )
+ )
+ (func $__growWasmMemory (; 11 ;) (param $newSize i32) (result i32)
+ (memory.grow
+ (local.get $newSize)
+ )
+ )
+)
+(;
+--BEGIN METADATA --
+{
+ "asmConsts": {
+ "2": ["{ Module.print(\"Got \" + $0); }", ["iii"], ["sync_on_main_thread_"]],
+ "0": ["{ Module.print(\"Hello world\"); }", ["iii"], ["sync_on_main_thread_"]],
+ "1": ["{ return $0 + $1; }", ["iii"], ["sync_on_main_thread_"]]
+ },
+ "staticBump": 84,
+ "tableSize": 1,
+ "initializers": [
+ "__wasm_call_ctors"
+ ],
+ "declares": [
+ ],
+ "externs": [
+ ],
+ "implementedFunctions": [
+ "___wasm_call_ctors",
+ "_main",
+ "_stackSave",
+ "_stackAlloc",
+ "_stackRestore",
+ "___growWasmMemory"
+ ],
+ "exports": [
+ "__wasm_call_ctors",
+ "main",
+ "stackSave",
+ "stackAlloc",
+ "stackRestore",
+ "__growWasmMemory"
+ ],
+ "namedGlobals": {
+ "__heap_base" : "66192",
+ "__data_end" : "652"
+ },
+ "invokeFuncs": [
+ ],
+ "features": [
+ ],
+ "mainReadsParams": 0
+}
+-- END METADATA --
+;)