diff options
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 62 | ||||
-rw-r--r-- | src/passes/pass.cpp | 1 | ||||
-rw-r--r-- | src/passes/passes.h | 1 | ||||
-rw-r--r-- | src/tools/wasm-reduce.cpp | 1 | ||||
-rw-r--r-- | test/passes/remove-unused-module-elements.txt | 19 | ||||
-rw-r--r-- | test/passes/remove-unused-module-elements.wast | 43 | ||||
-rw-r--r-- | test/passes/remove-unused-nonfunction-module-elements.txt | 331 | ||||
-rw-r--r-- | test/passes/remove-unused-nonfunction-module-elements.wast | 264 |
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) + ) + ) +) + |