summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2018-05-04 16:35:49 -0700
committerGitHub <noreply@github.com>2018-05-04 16:35:49 -0700
commitdbe49a6238f8a8df2d1a648fe0659916aad00c35 (patch)
tree17321d0a9699f258b1ac9c073b790bb8642d928b
parent21dc96b1852d6dbadedfec3336980be3f7bc7d45 (diff)
downloadbinaryen-dbe49a6238f8a8df2d1a648fe0659916aad00c35.tar.gz
binaryen-dbe49a6238f8a8df2d1a648fe0659916aad00c35.tar.bz2
binaryen-dbe49a6238f8a8df2d1a648fe0659916aad00c35.zip
improve remove-unused-module-elements (#1532)
Remove the entire memory/table when possible, in particular, when not imported, exported, or used. Previously we did not look at whether they were imported, so we assumed we could never remove them. Also add a variant that removes everything but functions, which can be useful when reducing a testcase that only cares about code in functions.
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp62
-rw-r--r--src/passes/pass.cpp1
-rw-r--r--src/passes/passes.h1
-rw-r--r--src/tools/wasm-reduce.cpp1
-rw-r--r--test/passes/remove-unused-module-elements.txt19
-rw-r--r--test/passes/remove-unused-module-elements.wast43
-rw-r--r--test/passes/remove-unused-nonfunction-module-elements.txt331
-rw-r--r--test/passes/remove-unused-nonfunction-module-elements.wast264
8 files changed, 709 insertions, 13 deletions
diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp
index 05e80af0d..6cd050da9 100644
--- a/src/passes/RemoveUnusedModuleElements.cpp
+++ b/src/passes/RemoveUnusedModuleElements.cpp
@@ -153,6 +153,10 @@ struct FunctionTypeAnalyzer : public PostWalker<FunctionTypeAnalyzer> {
};
struct RemoveUnusedModuleElements : public Pass {
+ bool rootAllFunctions;
+
+ RemoveUnusedModuleElements(bool rootAllFunctions) : rootAllFunctions(rootAllFunctions) {}
+
void run(PassRunner* runner, Module* module) override {
optimizeGlobalsAndFunctions(module);
optimizeFunctionTypes(module);
@@ -170,6 +174,12 @@ struct RemoveUnusedModuleElements : public Pass {
roots.emplace_back(ModuleElementKind::Function, module->start);
}
}
+ // If told to, root all the functions
+ if (rootAllFunctions) {
+ for (auto& func : module->functions) {
+ roots.emplace_back(ModuleElementKind::Function, func->name);
+ }
+ }
// Exports are roots.
bool exportsMemory = false;
bool exportsTable = false;
@@ -184,6 +194,16 @@ struct RemoveUnusedModuleElements : public Pass {
exportsTable = true;
}
}
+ // Check for special imports are roots.
+ bool importsMemory = false;
+ bool importsTable = false;
+ for (auto& curr : module->imports) {
+ if (curr->kind == ExternalKind::Memory) {
+ importsMemory = true;
+ } else if (curr->kind == ExternalKind::Table) {
+ importsTable = true;
+ }
+ }
// For now, all functions that can be called indirectly are marked as roots.
for (auto& segment : module->table.segments) {
for (auto& curr : segment.data) {
@@ -218,19 +238,31 @@ struct RemoveUnusedModuleElements : public Pass {
}
module->updateMaps();
// Handle the memory and table
- if (!exportsMemory && !analyzer.usesMemory && module->memory.segments.empty()) {
- module->memory.exists = false;
- module->memory.imported = false;
- module->memory.initial = 0;
- module->memory.max = 0;
- removeImport(ExternalKind::Memory, module);
+ if (!exportsMemory && !analyzer.usesMemory) {
+ if (!importsMemory) {
+ // The memory is unobservable to the outside, we can remove the contents.
+ module->memory.segments.clear();
+ }
+ if (module->memory.segments.empty()) {
+ module->memory.exists = false;
+ module->memory.imported = false;
+ module->memory.initial = 0;
+ module->memory.max = 0;
+ removeImport(ExternalKind::Memory, module);
+ }
}
- if (!exportsTable && !analyzer.usesTable && module->table.segments.empty()) {
- module->table.exists = false;
- module->table.imported = false;
- module->table.initial = 0;
- module->table.max = 0;
- removeImport(ExternalKind::Table, module);
+ if (!exportsTable && !analyzer.usesTable) {
+ if (!importsTable) {
+ // The table is unobservable to the outside, we can remove the contents.
+ module->table.segments.clear();
+ }
+ if (module->table.segments.empty()) {
+ module->table.exists = false;
+ module->table.imported = false;
+ module->table.initial = 0;
+ module->table.max = 0;
+ removeImport(ExternalKind::Table, module);
+ }
}
}
@@ -279,7 +311,11 @@ struct RemoveUnusedModuleElements : public Pass {
};
Pass* createRemoveUnusedModuleElementsPass() {
- return new RemoveUnusedModuleElements();
+ return new RemoveUnusedModuleElements(false);
+}
+
+Pass* createRemoveUnusedNonFunctionModuleElementsPass() {
+ return new RemoveUnusedModuleElements(true);
}
} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index ae9484d29..2d93d99ae 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -103,6 +103,7 @@ void PassRegistry::registerPasses() {
registerPass("remove-memory", "removes memory segments", createRemoveMemoryPass);
registerPass("remove-unused-brs", "removes breaks from locations that are not needed", createRemoveUnusedBrsPass);
registerPass("remove-unused-module-elements", "removes unused module elements", createRemoveUnusedModuleElementsPass);
+ registerPass("remove-unused-nonfunction-module-elements", "removes unused module elements that are not functions", createRemoveUnusedNonFunctionModuleElementsPass);
registerPass("remove-unused-names", "removes names from locations that are never branched to", createRemoveUnusedNamesPass);
registerPass("reorder-functions", "sorts functions by access frequency", createReorderFunctionsPass);
registerPass("reorder-locals", "sorts locals by access frequency", createReorderLocalsPass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 5ab03a439..cf77f9fac 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -60,6 +60,7 @@ Pass* createRemoveImportsPass();
Pass* createRemoveMemoryPass();
Pass* createRemoveUnusedBrsPass();
Pass* createRemoveUnusedModuleElementsPass();
+Pass* createRemoveUnusedNonFunctionModuleElementsPass();
Pass* createRemoveUnusedNamesPass();
Pass* createReorderFunctionsPass();
Pass* createReorderLocalsPass();
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index 6b8d93f97..7b7ff9da3 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -249,6 +249,7 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
"--remove-memory",
"--remove-unused-names --remove-unused-brs",
"--remove-unused-module-elements",
+ "--remove-unused-nonfunction-module-elements",
"--reorder-functions",
"--reorder-locals",
"--simplify-locals --vacuum",
diff --git a/test/passes/remove-unused-module-elements.txt b/test/passes/remove-unused-module-elements.txt
index 82ed9125e..7c85eec58 100644
--- a/test/passes/remove-unused-module-elements.txt
+++ b/test/passes/remove-unused-module-elements.txt
@@ -259,3 +259,22 @@
)
)
)
+(module
+)
+(module
+)
+(module
+ (type $0 (func (param f64) (result f64)))
+ (import "env" "table" (table 6 6 anyfunc))
+ (elem (i32.const 0) $0)
+ (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
+ (if (result f64)
+ (f64.eq
+ (f64.const 1)
+ (f64.const 1)
+ )
+ (f64.const 1)
+ (f64.const 0)
+ )
+ )
+)
diff --git a/test/passes/remove-unused-module-elements.wast b/test/passes/remove-unused-module-elements.wast
index 2d4b51f1b..dfefd7b91 100644
--- a/test/passes/remove-unused-module-elements.wast
+++ b/test/passes/remove-unused-module-elements.wast
@@ -218,4 +218,47 @@
(drop (i32.const 0))
)
)
+(module ;; the function and the table can be removed
+ (type $0 (func (param f64) (result f64)))
+ (table 6 6 anyfunc)
+ (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
+ (if (result f64)
+ (f64.eq
+ (f64.const 1)
+ (f64.const 1)
+ )
+ (f64.const 1)
+ (f64.const 0)
+ )
+ )
+)
+(module ;; the function uses the table, but all are removeable
+ (type $0 (func (param f64) (result f64)))
+ (table 6 6 anyfunc)
+ (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
+ (if (result f64)
+ (f64.eq
+ (f64.const 1)
+ (f64.const 1)
+ )
+ (call_indirect (type $0) (f64.const 1) (i32.const 0))
+ (f64.const 0)
+ )
+ )
+)
+(module ;; the table is imported - we can't remove it
+ (type $0 (func (param f64) (result f64)))
+ (import "env" "table" (table 6 6 anyfunc))
+ (elem (i32.const 0) $0)
+ (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
+ (if (result f64)
+ (f64.eq
+ (f64.const 1)
+ (f64.const 1)
+ )
+ (f64.const 1)
+ (f64.const 0)
+ )
+ )
+)
diff --git a/test/passes/remove-unused-nonfunction-module-elements.txt b/test/passes/remove-unused-nonfunction-module-elements.txt
new file mode 100644
index 000000000..bd4a8c7d4
--- /dev/null
+++ b/test/passes/remove-unused-nonfunction-module-elements.txt
@@ -0,0 +1,331 @@
+(module
+ (type $0 (func))
+ (type $1 (func (param i32)))
+ (type $2 (func (param i32) (result i32)))
+ (table 1 1 anyfunc)
+ (elem (i32.const 0) $called_indirect)
+ (memory $0 0)
+ (export "memory" (memory $0))
+ (export "exported" (func $exported))
+ (export "other1" (func $other1))
+ (export "other2" (func $other2))
+ (start $start)
+ (func $start (; 0 ;) (type $0)
+ (call $called0)
+ )
+ (func $called0 (; 1 ;) (type $0)
+ (call $called1)
+ )
+ (func $called1 (; 2 ;) (type $0)
+ (nop)
+ )
+ (func $called_indirect (; 3 ;) (type $0)
+ (nop)
+ )
+ (func $exported (; 4 ;) (type $0)
+ (call $called2)
+ )
+ (func $called2 (; 5 ;) (type $0)
+ (call $called2)
+ (call $called3)
+ )
+ (func $called3 (; 6 ;) (type $0)
+ (call $called4)
+ )
+ (func $called4 (; 7 ;) (type $0)
+ (call $called3)
+ )
+ (func $remove0 (; 8 ;) (type $0)
+ (call $remove1)
+ )
+ (func $remove1 (; 9 ;) (type $0)
+ (nop)
+ )
+ (func $remove2 (; 10 ;) (type $0)
+ (call $remove2)
+ )
+ (func $remove3 (; 11 ;) (type $0)
+ (call $remove4)
+ )
+ (func $remove4 (; 12 ;) (type $0)
+ (call $remove3)
+ )
+ (func $other1 (; 13 ;) (type $1) (param $0 i32)
+ (call_indirect (type $0)
+ (i32.const 0)
+ )
+ (call_indirect (type $0)
+ (i32.const 0)
+ )
+ (call_indirect (type $0)
+ (i32.const 0)
+ )
+ (call_indirect (type $0)
+ (i32.const 0)
+ )
+ (call_indirect (type $1)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ (call_indirect (type $1)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ (drop
+ (call_indirect (type $2)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call_indirect (type $2)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call_indirect (type $2)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ )
+ (func $other2 (; 14 ;) (type $1) (param $0 i32)
+ (unreachable)
+ )
+)
+(module
+)
+(module
+)
+(module
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 1 anyfunc))
+ (export "mem" (memory $0))
+ (export "tab" (table $0))
+)
+(module
+ (type $0 (func))
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 1 anyfunc))
+ (elem (i32.const 0) $waka)
+ (data (i32.const 1) "hello, world!")
+ (func $waka (; 0 ;) (type $0)
+ (nop)
+ )
+)
+(module
+ (type $0 (func))
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 0 anyfunc))
+ (export "user" (func $user))
+ (func $user (; 0 ;) (type $0)
+ (drop
+ (i32.load
+ (i32.const 0)
+ )
+ )
+ (call_indirect (type $0)
+ (i32.const 0)
+ )
+ )
+)
+(module
+ (type $0 (func))
+ (memory $0 (shared 23 256))
+ (export "user" (func $user))
+ (func $user (; 0 ;) (type $0)
+ (i32.store
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+)
+(module
+ (type $0 (func (result i32)))
+ (memory $0 (shared 23 256))
+ (export "user" (func $user))
+ (func $user (; 0 ;) (type $0) (result i32)
+ (i32.atomic.rmw.add
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+)
+(module
+ (type $0 (func (result i32)))
+ (memory $0 (shared 23 256))
+ (export "user" (func $user))
+ (func $user (; 0 ;) (type $0) (result i32)
+ (i32.atomic.rmw8_u.cmpxchg
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+)
+(module
+ (type $0 (func))
+ (memory $0 (shared 23 256))
+ (export "user" (func $user))
+ (func $user (; 0 ;) (type $0)
+ (local $0 i32)
+ (local $1 i64)
+ (drop
+ (i32.wait
+ (get_local $0)
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+)
+(module
+ (type $0 (func (result i32)))
+ (memory $0 (shared 23 256))
+ (export "user" (func $user))
+ (func $user (; 0 ;) (type $0) (result i32)
+ (wake
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+)
+(module
+ (type $0 (func (result i32)))
+ (memory $0 23 256)
+ (export "user" (func $user))
+ (func $user (; 0 ;) (type $0) (result i32)
+ (grow_memory
+ (i32.const 0)
+ )
+ )
+)
+(module
+ (type $0 (func (result i32)))
+ (import "env" "memory" (memory $0 256))
+ (export "user" (func $user))
+ (func $user (; 0 ;) (type $0) (result i32)
+ (grow_memory
+ (i32.const 0)
+ )
+ )
+)
+(module
+ (type $0 (func (result i32)))
+ (memory $0 23 256)
+ (export "user" (func $user))
+ (func $user (; 0 ;) (type $0) (result i32)
+ (current_memory)
+ )
+)
+(module
+ (type $0 (func))
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 0 anyfunc))
+ (import "env" "memoryBase" (global $memoryBase i32))
+ (import "env" "tableBase" (global $tableBase i32))
+ (elem (get_global $tableBase) $waka)
+ (data (get_global $memoryBase) "hello, world!")
+ (func $waka (; 0 ;) (type $0)
+ (nop)
+ )
+)
+(module
+ (type $FUNCSIG$ii (func (param i32) (result i32)))
+ (type $1 (func (result i32)))
+ (type $2 (func))
+ (import "env" "imported" (global $imported i32))
+ (import "env" "_puts" (func $_puts (param i32) (result i32)))
+ (global $int (mut i32) (get_global $imported))
+ (global $set (mut i32) (i32.const 100))
+ (global $exp_glob i32 (i32.const 600))
+ (export "one" (func $one))
+ (export "three" (func $three))
+ (export "exp_glob" (global $exp_glob))
+ (func $one (; 1 ;) (type $1) (result i32)
+ (call $two)
+ )
+ (func $two (; 2 ;) (type $1) (result i32)
+ (get_global $int)
+ )
+ (func $three (; 3 ;) (type $2)
+ (call $four)
+ )
+ (func $four (; 4 ;) (type $2)
+ (set_global $set
+ (i32.const 200)
+ )
+ (drop
+ (call $_puts
+ (i32.const 300)
+ )
+ )
+ )
+ (func $forget_implemented (; 5 ;) (type $2)
+ (nop)
+ )
+ (func $starter (; 6 ;) (type $2)
+ (nop)
+ )
+)
+(module
+ (type $0 (func))
+ (func $starter (; 0 ;) (type $0)
+ (nop)
+ )
+)
+(module
+ (type $0 (func))
+ (start $starter)
+ (func $starter (; 0 ;) (type $0)
+ (drop
+ (i32.const 0)
+ )
+ )
+)
+(module
+ (type $0 (func (param f64) (result f64)))
+ (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
+ (if (result f64)
+ (f64.eq
+ (f64.const 1)
+ (f64.const 1)
+ )
+ (f64.const 1)
+ (f64.const 0)
+ )
+ )
+)
+(module
+ (type $0 (func (param f64) (result f64)))
+ (table 6 6 anyfunc)
+ (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
+ (if (result f64)
+ (f64.eq
+ (f64.const 1)
+ (f64.const 1)
+ )
+ (call_indirect (type $0)
+ (f64.const 1)
+ (i32.const 0)
+ )
+ (f64.const 0)
+ )
+ )
+)
+(module
+ (type $0 (func (param f64) (result f64)))
+ (import "env" "table" (table 6 6 anyfunc))
+ (elem (i32.const 0) $0)
+ (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
+ (if (result f64)
+ (f64.eq
+ (f64.const 1)
+ (f64.const 1)
+ )
+ (f64.const 1)
+ (f64.const 0)
+ )
+ )
+)
diff --git a/test/passes/remove-unused-nonfunction-module-elements.wast b/test/passes/remove-unused-nonfunction-module-elements.wast
new file mode 100644
index 000000000..d0b49ee34
--- /dev/null
+++ b/test/passes/remove-unused-nonfunction-module-elements.wast
@@ -0,0 +1,264 @@
+(module
+ (memory 0)
+ (start $start)
+ (type $0 (func))
+ (type $0-dupe (func))
+ (type $1 (func (param i32)))
+ (type $1-dupe (func (param i32)))
+ (type $2 (func (param i32) (result i32)))
+ (type $2-dupe (func (param i32) (result i32)))
+ (type $2-thrupe (func (param i32) (result i32)))
+ (export "memory" (memory $0))
+ (export "exported" $exported)
+ (export "other1" $other1)
+ (export "other2" $other2)
+ (table 1 1 anyfunc)
+ (elem (i32.const 0) $called_indirect)
+ (func $start (type $0)
+ (call $called0)
+ )
+ (func $called0 (type $0)
+ (call $called1)
+ )
+ (func $called1 (type $0)
+ (nop)
+ )
+ (func $called_indirect (type $0)
+ (nop)
+ )
+ (func $exported (type $0-dupe)
+ (call $called2)
+ )
+ (func $called2 (type $0-dupe)
+ (call $called2)
+ (call $called3)
+ )
+ (func $called3 (type $0-dupe)
+ (call $called4)
+ )
+ (func $called4 (type $0-dupe)
+ (call $called3)
+ )
+ (func $remove0 (type $0-dupe)
+ (call $remove1)
+ )
+ (func $remove1 (type $0-dupe)
+ (nop)
+ )
+ (func $remove2 (type $0-dupe)
+ (call $remove2)
+ )
+ (func $remove3 (type $0)
+ (call $remove4)
+ )
+ (func $remove4 (type $0)
+ (call $remove3)
+ )
+ (func $other1 (param i32) (type $1)
+ (call_indirect (type $0) (i32.const 0))
+ (call_indirect (type $0) (i32.const 0))
+ (call_indirect (type $0-dupe) (i32.const 0))
+ (call_indirect (type $0-dupe) (i32.const 0))
+ (call_indirect (type $1) (i32.const 0) (i32.const 0))
+ (call_indirect (type $1-dupe) (i32.const 0) (i32.const 0))
+ (drop (call_indirect (type $2) (i32.const 0) (i32.const 0)))
+ (drop (call_indirect (type $2-dupe) (i32.const 0) (i32.const 0)))
+ (drop (call_indirect (type $2-thrupe) (i32.const 0) (i32.const 0)))
+ )
+ (func $other2 (param i32) (type $1-dupe)
+ (unreachable)
+ )
+)
+(module ;; remove the table and memory
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 0 anyfunc))
+)
+(module ;; also when not imported
+ (memory 256)
+ (table 1 anyfunc)
+)
+(module ;; but not when exported
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 1 anyfunc))
+ (export "mem" (memory 0))
+ (export "tab" (table 0))
+)
+(module ;; and not when there are segments
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 1 anyfunc))
+ (data (i32.const 1) "hello, world!")
+ (elem (i32.const 0) $waka)
+ (func $waka)
+)
+(module ;; and not when used
+ (type $0 (func))
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 0 anyfunc))
+ (export "user" $user)
+ (func $user
+ (drop (i32.load (i32.const 0)))
+ (call_indirect (type $0) (i32.const 0))
+ )
+)
+(module ;; more use checks
+ (memory $0 (shared 23 256))
+ (export "user" $user)
+ (func $user
+ (i32.store (i32.const 0) (i32.const 0))
+ )
+)
+(module ;; more use checks
+ (memory $0 (shared 23 256))
+ (export "user" $user)
+ (func $user (result i32)
+ (i32.atomic.rmw.add (i32.const 0) (i32.const 0))
+ )
+)
+(module ;; more use checks
+ (memory $0 (shared 23 256))
+ (export "user" $user)
+ (func $user (result i32)
+ (i32.atomic.rmw8_u.cmpxchg (i32.const 0) (i32.const 0) (i32.const 0))
+ )
+)
+(module ;; more use checks
+ (memory $0 (shared 23 256))
+ (export "user" $user)
+ (func $user
+ (local $0 i32)
+ (local $1 i64)
+ (drop
+ (i32.wait
+ (get_local $0)
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+)
+(module ;; more use checks
+ (memory $0 (shared 23 256))
+ (export "user" $user)
+ (func $user (result i32)
+ (wake (i32.const 0) (i32.const 0))
+ )
+)
+(module ;; more use checks
+ (memory $0 23 256)
+ (export "user" $user)
+ (func $user (result i32)
+ (grow_memory (i32.const 0))
+ )
+)
+(module ;; more use checks
+ (import "env" "memory" (memory $0 256))
+ (export "user" $user)
+ (func $user (result i32)
+ (grow_memory (i32.const 0))
+ )
+)
+(module ;; more use checks
+ (memory $0 23 256)
+ (export "user" $user)
+ (func $user (result i32)
+ (current_memory)
+ )
+)
+(module
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 0 anyfunc))
+ (import "env" "memoryBase" (global $memoryBase i32)) ;; used in init
+ (import "env" "tableBase" (global $tableBase i32)) ;; used in init
+ (data (get_global $memoryBase) "hello, world!")
+ (elem (get_global $tableBase) $waka)
+ (func $waka) ;; used in table
+)
+(module ;; one is exported, and one->two->int global, whose init->imported
+ (import "env" "imported" (global $imported i32))
+ (import "env" "forgetme" (global $forgetme i32))
+ (import "env" "_puts" (func $_puts (param i32) (result i32)))
+ (import "env" "forget_puts" (func $forget_puts (param i32) (result i32)))
+ (global $int (mut i32) (get_global $imported))
+ (global $set (mut i32) (i32.const 100))
+ (global $forget_global (mut i32) (i32.const 500))
+ (global $exp_glob i32 (i32.const 600))
+ (export "one" (func $one))
+ (export "three" (func $three))
+ (export "exp_glob" (global $exp_glob))
+ (start $starter)
+ (func $one (result i32)
+ (call $two)
+ )
+ (func $two (result i32)
+ (get_global $int)
+ )
+ (func $three
+ (call $four)
+ )
+ (func $four
+ (set_global $set (i32.const 200))
+ (drop (call $_puts (i32.const 300)))
+ )
+ (func $forget_implemented
+ (nop)
+ )
+ (func $starter
+ (nop)
+ )
+)
+(module ;; empty start being removed
+ (start $starter)
+ (func $starter
+ (nop)
+ )
+)
+(module ;; non-empty start being kept
+ (start $starter)
+ (func $starter
+ (drop (i32.const 0))
+ )
+)
+(module ;; the function stays but the table can be removed
+ (type $0 (func (param f64) (result f64)))
+ (table 6 6 anyfunc)
+ (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
+ (if (result f64)
+ (f64.eq
+ (f64.const 1)
+ (f64.const 1)
+ )
+ (f64.const 1)
+ (f64.const 0)
+ )
+ )
+)
+(module ;; the function keeps the table alive
+ (type $0 (func (param f64) (result f64)))
+ (table 6 6 anyfunc)
+ (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
+ (if (result f64)
+ (f64.eq
+ (f64.const 1)
+ (f64.const 1)
+ )
+ (call_indirect (type $0) (f64.const 1) (i32.const 0))
+ (f64.const 0)
+ )
+ )
+)
+(module ;; the table is imported - we can't remove it
+ (type $0 (func (param f64) (result f64)))
+ (import "env" "table" (table 6 6 anyfunc))
+ (elem (i32.const 0) $0)
+ (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
+ (if (result f64)
+ (f64.eq
+ (f64.const 1)
+ (f64.const 1)
+ )
+ (f64.const 1)
+ (f64.const 0)
+ )
+ )
+)
+