summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/table-utils.h43
-rw-r--r--src/passes/Directize.cpp6
-rw-r--r--src/passes/PostEmscripten.cpp7
-rw-r--r--src/tools/wasm-emscripten-finalize.cpp13
-rw-r--r--src/wasm-emscripten.h2
-rw-r--r--src/wasm/wasm-emscripten.cpp49
-rw-r--r--src/wasm2js.h2
-rw-r--r--test/lld/main_module_table.wat9
-rw-r--r--test/lld/main_module_table.wat.out106
-rw-r--r--test/lld/main_module_table_2.wat10
-rw-r--r--test/lld/main_module_table_2.wat.out106
-rw-r--r--test/lld/main_module_table_3.wat11
-rw-r--r--test/lld/main_module_table_3.wat.out106
-rw-r--r--test/lld/main_module_table_4.wat12
-rw-r--r--test/lld/main_module_table_4.wat.out108
-rw-r--r--test/lld/main_module_table_5.wat16
-rw-r--r--test/lld/main_module_table_5.wat.out114
17 files changed, 696 insertions, 24 deletions
diff --git a/src/ir/table-utils.h b/src/ir/table-utils.h
index d2dc3a7d5..3c49ab16a 100644
--- a/src/ir/table-utils.h
+++ b/src/ir/table-utils.h
@@ -17,11 +17,14 @@
#ifndef wasm_ir_table_h
#define wasm_ir_table_h
+#include "ir/literal-utils.h"
#include "wasm-traversal.h"
#include "wasm.h"
namespace wasm {
+namespace TableUtils {
+
struct FlatTable {
std::vector<Name> names;
bool valid;
@@ -47,6 +50,46 @@ struct FlatTable {
}
};
+// Ensure one table segment exists. This adds the table if necessary, then
+// adds a segment if we need one.
+inline Table::Segment& ensureTableWithOneSegment(Table& table, Module& wasm) {
+ table.exists = true;
+ if (table.segments.size() == 0) {
+ table.segments.resize(1);
+ table.segments[0].offset = LiteralUtils::makeZero(Type::i32, wasm);
+ }
+ if (table.segments.size() != 1) {
+ Fatal() << "can't ensure 1 segment";
+ }
+ return table.segments[0];
+}
+
+// Appends a name to the table. This assumes the table has 0 or 1 segments,
+// as with 2 or more it's ambiguous what we should do (use a hole in the middle
+// or not).
+inline Index append(Table& table, Name name, Module& wasm) {
+ auto& segment = ensureTableWithOneSegment(table, wasm);
+ table.segments[0];
+ auto tableIndex = segment.data.size();
+ segment.data.push_back(name);
+ table.initial = table.initial + 1;
+ return tableIndex;
+}
+
+// Checks if a function is already in the table. Returns that index if so,
+// otherwise appends it.
+inline Index getOrAppend(Table& table, Name name, Module& wasm) {
+ auto& segment = ensureTableWithOneSegment(table, wasm);
+ for (Index i = 0; i < segment.data.size(); i++) {
+ if (segment.data[i] == name) {
+ return i;
+ }
+ }
+ return append(table, name, wasm);
+}
+
+} // namespace TableUtils
+
} // namespace wasm
#endif // wasm_ir_table_h
diff --git a/src/passes/Directize.cpp b/src/passes/Directize.cpp
index 52aa7e087..037eca0da 100644
--- a/src/passes/Directize.cpp
+++ b/src/passes/Directize.cpp
@@ -39,7 +39,7 @@ struct FunctionDirectizer : public WalkerPass<PostWalker<FunctionDirectizer>> {
Pass* create() override { return new FunctionDirectizer(flatTable); }
- FunctionDirectizer(FlatTable* flatTable) : flatTable(flatTable) {}
+ FunctionDirectizer(TableUtils::FlatTable* flatTable) : flatTable(flatTable) {}
void visitCallIndirect(CallIndirect* curr) {
if (auto* c = curr->target->dynCast<Const>()) {
@@ -77,7 +77,7 @@ struct FunctionDirectizer : public WalkerPass<PostWalker<FunctionDirectizer>> {
}
private:
- FlatTable* flatTable;
+ TableUtils::FlatTable* flatTable;
bool changedTypes = false;
void replaceWithUnreachable(CallIndirect* call) {
@@ -104,7 +104,7 @@ struct Directize : public Pass {
return;
}
}
- FlatTable flatTable(module->table);
+ TableUtils::FlatTable flatTable(module->table);
if (!flatTable.valid) {
return;
}
diff --git a/src/passes/PostEmscripten.cpp b/src/passes/PostEmscripten.cpp
index ccf985c50..8efaae8c5 100644
--- a/src/passes/PostEmscripten.cpp
+++ b/src/passes/PostEmscripten.cpp
@@ -135,7 +135,7 @@ struct PostEmscripten : public Pass {
// Next, see if the Table is flat, which we need in order to see where
// invokes go statically. (In dynamic linking, the table is not flat,
// and we can't do this.)
- FlatTable flatTable(module->table);
+ TableUtils::FlatTable flatTable(module->table);
if (!flatTable.valid) {
return;
}
@@ -167,9 +167,10 @@ struct PostEmscripten : public Pass {
Pass* create() override { return new OptimizeInvokes(map, flatTable); }
std::map<Function*, Info>& map;
- FlatTable& flatTable;
+ TableUtils::FlatTable& flatTable;
- OptimizeInvokes(std::map<Function*, Info>& map, FlatTable& flatTable)
+ OptimizeInvokes(std::map<Function*, Info>& map,
+ TableUtils::FlatTable& flatTable)
: map(map), flatTable(flatTable) {}
void visitCall(Call* curr) {
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp
index 9ade001ef..576b64a1a 100644
--- a/src/tools/wasm-emscripten-finalize.cpp
+++ b/src/tools/wasm-emscripten-finalize.cpp
@@ -50,7 +50,7 @@ int main(int argc, const char* argv[]) {
bool emitBinary = true;
bool debugInfo = false;
bool DWARF = false;
- bool isSideModule = false;
+ bool sideModule = false;
bool legalizeJavaScriptFFI = true;
bool checkStackOverflow = false;
uint64_t globalBase = INVALID_BASE;
@@ -100,8 +100,8 @@ int main(int argc, const char* argv[]) {
"",
"Input is an emscripten side module",
Options::Arguments::Zero,
- [&isSideModule](Options* o, const std::string& argument) {
- isSideModule = true;
+ [&sideModule](Options* o, const std::string& argument) {
+ sideModule = true;
})
.add("--input-source-map",
"-ism",
@@ -191,7 +191,7 @@ int main(int argc, const char* argv[]) {
uint32_t dataSize = 0;
- if (!isSideModule) {
+ if (!sideModule) {
if (globalBase == INVALID_BASE) {
Fatal() << "globalBase must be set";
}
@@ -215,6 +215,7 @@ int main(int argc, const char* argv[]) {
EmscriptenGlueGenerator generator(wasm);
generator.setStandalone(standaloneWasm);
+ generator.setSideModule(sideModule);
generator.fixInvokeFunctionNames();
@@ -232,11 +233,11 @@ int main(int argc, const char* argv[]) {
}
wasm.updateMaps();
- if (checkStackOverflow && !isSideModule) {
+ if (checkStackOverflow && !sideModule) {
generator.enforceStackLimit();
}
- if (isSideModule) {
+ if (sideModule) {
BYN_TRACE("finalizing as side module\n");
generator.replaceStackPointerGlobal();
generator.generatePostInstantiateFunction();
diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h
index c7689b4bc..80277cb18 100644
--- a/src/wasm-emscripten.h
+++ b/src/wasm-emscripten.h
@@ -32,6 +32,7 @@ public:
useStackPointerGlobal(stackPointerOffset == 0) {}
void setStandalone(bool standalone_) { standalone = standalone_; }
+ void setSideModule(bool sideModule_) { sideModule = sideModule_; }
void generateRuntimeFunctions();
Function* generateMemoryGrowthFunction();
@@ -72,6 +73,7 @@ private:
Address stackPointerOffset;
bool useStackPointerGlobal;
bool standalone;
+ bool sideModule;
// Used by generateDynCallThunk to track all the dynCall functions created
// so far.
std::unordered_set<Signature> sigs;
diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp
index 8357983b2..5c411b454 100644
--- a/src/wasm/wasm-emscripten.cpp
+++ b/src/wasm/wasm-emscripten.cpp
@@ -22,7 +22,9 @@
#include "asmjs/shared-constants.h"
#include "ir/import-utils.h"
#include "ir/literal-utils.h"
+#include "ir/manipulation.h"
#include "ir/module-utils.h"
+#include "ir/table-utils.h"
#include "shared-constants.h"
#include "support/debug.h"
#include "wasm-builder.h"
@@ -214,12 +216,12 @@ void EmscriptenGlueGenerator::generateRuntimeFunctions() {
static Function*
ensureFunctionImport(Module* module, Name name, Signature sig) {
- // Then see if its already imported
+ // See if its already imported.
ImportInfo info(*module);
- if (Function* f = info.getImportedFunction(ENV, name)) {
+ if (auto* f = info.getImportedFunction(ENV, name)) {
return f;
}
- // Failing that create a new function import.
+ // Failing that create a new import.
auto import = new Function;
import->name = name;
import->module = ENV;
@@ -277,6 +279,8 @@ Function* EmscriptenGlueGenerator::generateAssignGOTEntriesFunction() {
block->list.push_back(globalSet);
}
+ ImportInfo importInfo(wasm);
+
for (Global* g : gotFuncEntries) {
Function* f = nullptr;
// The function has to exist either as export or an import.
@@ -286,20 +290,43 @@ Function* EmscriptenGlueGenerator::generateAssignGOTEntriesFunction() {
if (ex) {
assert(ex->kind == ExternalKind::Function);
f = wasm.getFunction(ex->value);
- } else {
- ImportInfo info(wasm);
- f = info.getImportedFunction(ENV, g->base);
- if (!f) {
- Fatal() << "GOT.func entry with no import/export: " << g->base;
+ if (!sideModule) {
+ // This is exported, so must be one of the functions implemented here.
+ // Simply add it to the table, and use that index. The loader will
+ // know to reuse that index for other modules so they all share the
+ // same index and function pointer equality works.
+ // We may be able to do something for side modules as well, however,
+ // that would require at least updating the dylink section.
+ if (f->imported()) {
+ Fatal() << "GOT.func entry is both imported and exported: "
+ << g->base;
+ }
+ auto tableIndex = TableUtils::getOrAppend(wasm.table, f->name, wasm);
+ auto* c = LiteralUtils::makeFromInt32(tableIndex, Type::i32, wasm);
+ // The base relative to which we are computed is the offset of the
+ // singleton segment.
+ auto* getBase =
+ ExpressionManipulator::copy(wasm.table.segments[0].offset, wasm);
+ auto* add = builder.makeBinary(AddInt32, getBase, c);
+ auto* globalSet = builder.makeGlobalSet(g->name, add);
+ block->list.push_back(globalSet);
+ continue;
}
+ // Otherwise, this is a side module, and fall through to join the case
+ // of an import.
+ } else {
+ // This is imported. Create an fp$ import to get the function table index.
+ f = importInfo.getImportedFunction(ENV, g->base);
+ }
+ if (!f) {
+ Fatal() << "GOT.func entry with no import/export: " << g->base;
}
-
Name getter(
(std::string("fp$") + g->base.c_str() + std::string("$") + getSig(f))
.c_str());
ensureFunctionImport(&wasm, getter, Signature(Type::none, Type::i32));
- Expression* call = builder.makeCall(getter, {}, Type::i32);
- GlobalSet* globalSet = builder.makeGlobalSet(g->name, call);
+ auto* call = builder.makeCall(getter, {}, Type::i32);
+ auto* globalSet = builder.makeGlobalSet(g->name, call);
block->list.push_back(globalSet);
}
diff --git a/src/wasm2js.h b/src/wasm2js.h
index cdc109cfa..cc2cfa128 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -530,7 +530,7 @@ void Wasm2JSBuilder::addGlobalImport(Ref ast, Global* import) {
void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) {
// Emit a simple flat table as a JS array literal. Otherwise,
// emit assignments separately for each index.
- FlatTable flat(wasm->table);
+ TableUtils::FlatTable flat(wasm->table);
if (flat.valid && !wasm->table.imported()) {
Ref theVar = ValueBuilder::makeVar();
ast->push_back(theVar);
diff --git a/test/lld/main_module_table.wat b/test/lld/main_module_table.wat
new file mode 100644
index 000000000..b63b4d8fe
--- /dev/null
+++ b/test/lld/main_module_table.wat
@@ -0,0 +1,9 @@
+(module
+ (import "env" "__stack_pointer" (global $sp (mut i32)))
+ (import "GOT.func" "__stdio_write" (global $gimport$9 (mut i32)))
+ (global $global i32 (i32.const 42))
+ (export "__stdio_write" (func $__stdio_write))
+ (export "__data_end" (global $global))
+ (func $__stdio_write
+ )
+)
diff --git a/test/lld/main_module_table.wat.out b/test/lld/main_module_table.wat.out
new file mode 100644
index 000000000..fd292809f
--- /dev/null
+++ b/test/lld/main_module_table.wat.out
@@ -0,0 +1,106 @@
+(module
+ (type $none_=>_none (func))
+ (type $i32_=>_none (func (param i32)))
+ (type $i32_=>_i32 (func (param i32) (result i32)))
+ (type $none_=>_i32 (func (result i32)))
+ (import "env" "__stack_pointer" (global $sp_import i32))
+ (table $0 1 funcref)
+ (elem (i32.const 0) $__stdio_write)
+ (global $gimport$9 (mut i32) (i32.const 0))
+ (global $global i32 (i32.const 42))
+ (global $sp (mut i32) (global.get $sp_import))
+ (export "__stdio_write" (func $__stdio_write))
+ (export "__data_end" (global $global))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (export "__assign_got_enties" (func $__assign_got_enties))
+ (export "dynCall_v" (func $dynCall_v))
+ (func $__stdio_write (; 0 ;)
+ (nop)
+ )
+ (func $stackSave (; 1 ;) (result i32)
+ (global.get $sp)
+ )
+ (func $stackAlloc (; 2 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (global.set $sp
+ (local.tee $1
+ (i32.and
+ (i32.sub
+ (global.get $sp)
+ (local.get $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (local.get $1)
+ )
+ (func $stackRestore (; 3 ;) (param $0 i32)
+ (global.set $sp
+ (local.get $0)
+ )
+ )
+ (func $__growWasmMemory (; 4 ;) (param $newSize i32) (result i32)
+ (memory.grow
+ (local.get $newSize)
+ )
+ )
+ (func $__assign_got_enties (; 5 ;)
+ (global.set $gimport$9
+ (i32.add
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ )
+ (func $dynCall_v (; 6 ;) (param $fptr i32)
+ (call_indirect (type $none_=>_none)
+ (local.get $fptr)
+ )
+ )
+)
+(;
+--BEGIN METADATA --
+{
+ "staticBump": 4294966770,
+ "tableSize": 1,
+ "initializers": [
+ "__assign_got_enties"
+ ],
+ "declares": [
+ ],
+ "externs": [
+ "___stack_pointer"
+ ],
+ "implementedFunctions": [
+ "___stdio_write",
+ "_stackSave",
+ "_stackAlloc",
+ "_stackRestore",
+ "___growWasmMemory",
+ "___assign_got_enties",
+ "_dynCall_v"
+ ],
+ "exports": [
+ "__stdio_write",
+ "stackSave",
+ "stackAlloc",
+ "stackRestore",
+ "__growWasmMemory",
+ "__assign_got_enties",
+ "dynCall_v"
+ ],
+ "namedGlobals": {
+ "__data_end" : "42"
+ },
+ "invokeFuncs": [
+ ],
+ "features": [
+ ],
+ "mainReadsParams": 0
+}
+-- END METADATA --
+;)
diff --git a/test/lld/main_module_table_2.wat b/test/lld/main_module_table_2.wat
new file mode 100644
index 000000000..2ccc1fbcc
--- /dev/null
+++ b/test/lld/main_module_table_2.wat
@@ -0,0 +1,10 @@
+(module
+ (import "env" "__stack_pointer" (global $sp (mut i32)))
+ (import "GOT.func" "__stdio_write" (global $gimport$9 (mut i32)))
+ (import "env" "table" (table $timport$9 1 funcref))
+ (global $global i32 (i32.const 42))
+ (export "__stdio_write" (func $__stdio_write))
+ (export "__data_end" (global $global))
+ (func $__stdio_write
+ )
+)
diff --git a/test/lld/main_module_table_2.wat.out b/test/lld/main_module_table_2.wat.out
new file mode 100644
index 000000000..92a40d5f2
--- /dev/null
+++ b/test/lld/main_module_table_2.wat.out
@@ -0,0 +1,106 @@
+(module
+ (type $none_=>_none (func))
+ (type $i32_=>_none (func (param i32)))
+ (type $i32_=>_i32 (func (param i32) (result i32)))
+ (type $none_=>_i32 (func (result i32)))
+ (import "env" "table" (table $0 2 funcref))
+ (elem (i32.const 0) $__stdio_write)
+ (import "env" "__stack_pointer" (global $sp_import i32))
+ (global $gimport$9 (mut i32) (i32.const 0))
+ (global $global i32 (i32.const 42))
+ (global $sp (mut i32) (global.get $sp_import))
+ (export "__stdio_write" (func $__stdio_write))
+ (export "__data_end" (global $global))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (export "__assign_got_enties" (func $__assign_got_enties))
+ (export "dynCall_v" (func $dynCall_v))
+ (func $__stdio_write (; 0 ;)
+ (nop)
+ )
+ (func $stackSave (; 1 ;) (result i32)
+ (global.get $sp)
+ )
+ (func $stackAlloc (; 2 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (global.set $sp
+ (local.tee $1
+ (i32.and
+ (i32.sub
+ (global.get $sp)
+ (local.get $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (local.get $1)
+ )
+ (func $stackRestore (; 3 ;) (param $0 i32)
+ (global.set $sp
+ (local.get $0)
+ )
+ )
+ (func $__growWasmMemory (; 4 ;) (param $newSize i32) (result i32)
+ (memory.grow
+ (local.get $newSize)
+ )
+ )
+ (func $__assign_got_enties (; 5 ;)
+ (global.set $gimport$9
+ (i32.add
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ )
+ (func $dynCall_v (; 6 ;) (param $fptr i32)
+ (call_indirect (type $none_=>_none)
+ (local.get $fptr)
+ )
+ )
+)
+(;
+--BEGIN METADATA --
+{
+ "staticBump": 4294966770,
+ "tableSize": 2,
+ "initializers": [
+ "__assign_got_enties"
+ ],
+ "declares": [
+ ],
+ "externs": [
+ "___stack_pointer"
+ ],
+ "implementedFunctions": [
+ "___stdio_write",
+ "_stackSave",
+ "_stackAlloc",
+ "_stackRestore",
+ "___growWasmMemory",
+ "___assign_got_enties",
+ "_dynCall_v"
+ ],
+ "exports": [
+ "__stdio_write",
+ "stackSave",
+ "stackAlloc",
+ "stackRestore",
+ "__growWasmMemory",
+ "__assign_got_enties",
+ "dynCall_v"
+ ],
+ "namedGlobals": {
+ "__data_end" : "42"
+ },
+ "invokeFuncs": [
+ ],
+ "features": [
+ ],
+ "mainReadsParams": 0
+}
+-- END METADATA --
+;)
diff --git a/test/lld/main_module_table_3.wat b/test/lld/main_module_table_3.wat
new file mode 100644
index 000000000..2f7d92ebf
--- /dev/null
+++ b/test/lld/main_module_table_3.wat
@@ -0,0 +1,11 @@
+(module
+ (import "env" "__stack_pointer" (global $sp (mut i32)))
+ (import "GOT.func" "__stdio_write" (global $gimport$9 (mut i32)))
+ (import "env" "table" (table $timport$9 1 funcref))
+ (elem (i32.const 0))
+ (global $global i32 (i32.const 42))
+ (export "__stdio_write" (func $__stdio_write))
+ (export "__data_end" (global $global))
+ (func $__stdio_write
+ )
+)
diff --git a/test/lld/main_module_table_3.wat.out b/test/lld/main_module_table_3.wat.out
new file mode 100644
index 000000000..92a40d5f2
--- /dev/null
+++ b/test/lld/main_module_table_3.wat.out
@@ -0,0 +1,106 @@
+(module
+ (type $none_=>_none (func))
+ (type $i32_=>_none (func (param i32)))
+ (type $i32_=>_i32 (func (param i32) (result i32)))
+ (type $none_=>_i32 (func (result i32)))
+ (import "env" "table" (table $0 2 funcref))
+ (elem (i32.const 0) $__stdio_write)
+ (import "env" "__stack_pointer" (global $sp_import i32))
+ (global $gimport$9 (mut i32) (i32.const 0))
+ (global $global i32 (i32.const 42))
+ (global $sp (mut i32) (global.get $sp_import))
+ (export "__stdio_write" (func $__stdio_write))
+ (export "__data_end" (global $global))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (export "__assign_got_enties" (func $__assign_got_enties))
+ (export "dynCall_v" (func $dynCall_v))
+ (func $__stdio_write (; 0 ;)
+ (nop)
+ )
+ (func $stackSave (; 1 ;) (result i32)
+ (global.get $sp)
+ )
+ (func $stackAlloc (; 2 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (global.set $sp
+ (local.tee $1
+ (i32.and
+ (i32.sub
+ (global.get $sp)
+ (local.get $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (local.get $1)
+ )
+ (func $stackRestore (; 3 ;) (param $0 i32)
+ (global.set $sp
+ (local.get $0)
+ )
+ )
+ (func $__growWasmMemory (; 4 ;) (param $newSize i32) (result i32)
+ (memory.grow
+ (local.get $newSize)
+ )
+ )
+ (func $__assign_got_enties (; 5 ;)
+ (global.set $gimport$9
+ (i32.add
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ )
+ (func $dynCall_v (; 6 ;) (param $fptr i32)
+ (call_indirect (type $none_=>_none)
+ (local.get $fptr)
+ )
+ )
+)
+(;
+--BEGIN METADATA --
+{
+ "staticBump": 4294966770,
+ "tableSize": 2,
+ "initializers": [
+ "__assign_got_enties"
+ ],
+ "declares": [
+ ],
+ "externs": [
+ "___stack_pointer"
+ ],
+ "implementedFunctions": [
+ "___stdio_write",
+ "_stackSave",
+ "_stackAlloc",
+ "_stackRestore",
+ "___growWasmMemory",
+ "___assign_got_enties",
+ "_dynCall_v"
+ ],
+ "exports": [
+ "__stdio_write",
+ "stackSave",
+ "stackAlloc",
+ "stackRestore",
+ "__growWasmMemory",
+ "__assign_got_enties",
+ "dynCall_v"
+ ],
+ "namedGlobals": {
+ "__data_end" : "42"
+ },
+ "invokeFuncs": [
+ ],
+ "features": [
+ ],
+ "mainReadsParams": 0
+}
+-- END METADATA --
+;)
diff --git a/test/lld/main_module_table_4.wat b/test/lld/main_module_table_4.wat
new file mode 100644
index 000000000..a991de4bb
--- /dev/null
+++ b/test/lld/main_module_table_4.wat
@@ -0,0 +1,12 @@
+(module
+ (import "env" "__stack_pointer" (global $sp (mut i32)))
+ (import "GOT.func" "__stdio_write" (global $gimport$9 (mut i32)))
+ (import "env" "__table_base" (global $tb i32))
+ (import "env" "table" (table $timport$9 1 funcref))
+ (elem (global.get $tb))
+ (global $global i32 (i32.const 42))
+ (export "__stdio_write" (func $__stdio_write))
+ (export "__data_end" (global $global))
+ (func $__stdio_write
+ )
+)
diff --git a/test/lld/main_module_table_4.wat.out b/test/lld/main_module_table_4.wat.out
new file mode 100644
index 000000000..db2ef3cd8
--- /dev/null
+++ b/test/lld/main_module_table_4.wat.out
@@ -0,0 +1,108 @@
+(module
+ (type $none_=>_none (func))
+ (type $i32_=>_none (func (param i32)))
+ (type $i32_=>_i32 (func (param i32) (result i32)))
+ (type $none_=>_i32 (func (result i32)))
+ (import "env" "table" (table $0 2 funcref))
+ (elem (global.get $tb) $__stdio_write)
+ (import "env" "__stack_pointer" (global $sp_import i32))
+ (import "env" "__table_base" (global $tb i32))
+ (global $gimport$9 (mut i32) (i32.const 0))
+ (global $global i32 (i32.const 42))
+ (global $sp (mut i32) (global.get $sp_import))
+ (export "__stdio_write" (func $__stdio_write))
+ (export "__data_end" (global $global))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (export "__assign_got_enties" (func $__assign_got_enties))
+ (export "dynCall_v" (func $dynCall_v))
+ (func $__stdio_write (; 0 ;)
+ (nop)
+ )
+ (func $stackSave (; 1 ;) (result i32)
+ (global.get $sp)
+ )
+ (func $stackAlloc (; 2 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (global.set $sp
+ (local.tee $1
+ (i32.and
+ (i32.sub
+ (global.get $sp)
+ (local.get $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (local.get $1)
+ )
+ (func $stackRestore (; 3 ;) (param $0 i32)
+ (global.set $sp
+ (local.get $0)
+ )
+ )
+ (func $__growWasmMemory (; 4 ;) (param $newSize i32) (result i32)
+ (memory.grow
+ (local.get $newSize)
+ )
+ )
+ (func $__assign_got_enties (; 5 ;)
+ (global.set $gimport$9
+ (i32.add
+ (global.get $tb)
+ (i32.const 0)
+ )
+ )
+ )
+ (func $dynCall_v (; 6 ;) (param $fptr i32)
+ (call_indirect (type $none_=>_none)
+ (local.get $fptr)
+ )
+ )
+)
+(;
+--BEGIN METADATA --
+{
+ "staticBump": 4294966770,
+ "tableSize": 2,
+ "initializers": [
+ "__assign_got_enties"
+ ],
+ "declares": [
+ ],
+ "externs": [
+ "___stack_pointer",
+ "___table_base"
+ ],
+ "implementedFunctions": [
+ "___stdio_write",
+ "_stackSave",
+ "_stackAlloc",
+ "_stackRestore",
+ "___growWasmMemory",
+ "___assign_got_enties",
+ "_dynCall_v"
+ ],
+ "exports": [
+ "__stdio_write",
+ "stackSave",
+ "stackAlloc",
+ "stackRestore",
+ "__growWasmMemory",
+ "__assign_got_enties",
+ "dynCall_v"
+ ],
+ "namedGlobals": {
+ "__data_end" : "42"
+ },
+ "invokeFuncs": [
+ ],
+ "features": [
+ ],
+ "mainReadsParams": 0
+}
+-- END METADATA --
+;)
diff --git a/test/lld/main_module_table_5.wat b/test/lld/main_module_table_5.wat
new file mode 100644
index 000000000..ee15deee5
--- /dev/null
+++ b/test/lld/main_module_table_5.wat
@@ -0,0 +1,16 @@
+(module
+ (import "env" "__stack_pointer" (global $sp (mut i32)))
+ (import "GOT.func" "__stdio_write" (global $gimport$9 (mut i32)))
+ (import "env" "__table_base" (global $tb i32))
+ (import "env" "table" (table $timport$9 1 funcref))
+ (elem (global.get $tb) $other $stuff)
+ (global $global i32 (i32.const 42))
+ (export "__stdio_write" (func $__stdio_write))
+ (export "__data_end" (global $global))
+ (func $__stdio_write
+ )
+ (func $other
+ )
+ (func $stuff
+ )
+)
diff --git a/test/lld/main_module_table_5.wat.out b/test/lld/main_module_table_5.wat.out
new file mode 100644
index 000000000..590164b0f
--- /dev/null
+++ b/test/lld/main_module_table_5.wat.out
@@ -0,0 +1,114 @@
+(module
+ (type $none_=>_none (func))
+ (type $i32_=>_none (func (param i32)))
+ (type $i32_=>_i32 (func (param i32) (result i32)))
+ (type $none_=>_i32 (func (result i32)))
+ (import "env" "table" (table $0 2 funcref))
+ (elem (global.get $tb) $other $stuff $__stdio_write)
+ (import "env" "__stack_pointer" (global $sp_import i32))
+ (import "env" "__table_base" (global $tb i32))
+ (global $gimport$9 (mut i32) (i32.const 0))
+ (global $global i32 (i32.const 42))
+ (global $sp (mut i32) (global.get $sp_import))
+ (export "__stdio_write" (func $__stdio_write))
+ (export "__data_end" (global $global))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (export "__assign_got_enties" (func $__assign_got_enties))
+ (export "dynCall_v" (func $dynCall_v))
+ (func $__stdio_write (; 0 ;)
+ (nop)
+ )
+ (func $other (; 1 ;)
+ (nop)
+ )
+ (func $stuff (; 2 ;)
+ (nop)
+ )
+ (func $stackSave (; 3 ;) (result i32)
+ (global.get $sp)
+ )
+ (func $stackAlloc (; 4 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (global.set $sp
+ (local.tee $1
+ (i32.and
+ (i32.sub
+ (global.get $sp)
+ (local.get $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (local.get $1)
+ )
+ (func $stackRestore (; 5 ;) (param $0 i32)
+ (global.set $sp
+ (local.get $0)
+ )
+ )
+ (func $__growWasmMemory (; 6 ;) (param $newSize i32) (result i32)
+ (memory.grow
+ (local.get $newSize)
+ )
+ )
+ (func $__assign_got_enties (; 7 ;)
+ (global.set $gimport$9
+ (i32.add
+ (global.get $tb)
+ (i32.const 2)
+ )
+ )
+ )
+ (func $dynCall_v (; 8 ;) (param $fptr i32)
+ (call_indirect (type $none_=>_none)
+ (local.get $fptr)
+ )
+ )
+)
+(;
+--BEGIN METADATA --
+{
+ "staticBump": 4294966770,
+ "tableSize": 2,
+ "initializers": [
+ "__assign_got_enties"
+ ],
+ "declares": [
+ ],
+ "externs": [
+ "___stack_pointer",
+ "___table_base"
+ ],
+ "implementedFunctions": [
+ "___stdio_write",
+ "_stackSave",
+ "_stackAlloc",
+ "_stackRestore",
+ "___growWasmMemory",
+ "___assign_got_enties",
+ "_dynCall_v"
+ ],
+ "exports": [
+ "__stdio_write",
+ "stackSave",
+ "stackAlloc",
+ "stackRestore",
+ "__growWasmMemory",
+ "__assign_got_enties",
+ "dynCall_v"
+ ],
+ "namedGlobals": {
+ "__data_end" : "42"
+ },
+ "invokeFuncs": [
+ ],
+ "features": [
+ ],
+ "mainReadsParams": 0
+}
+-- END METADATA --
+;)