diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 6ddf2cab6fe..ff9ba10ea31 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -2393,7 +2393,6 @@ def write_commands(commands, filename): ("--abstract-type-refining",), ("--cfp",), ("--cfp-reftest",), - ("--gsi",), ("--type-ssa",), ("--type-merging",)} diff --git a/src/passes/GlobalStructInference.cpp b/src/passes/GlobalStructInference.cpp index 77badc2148e..ca6d7adfaf5 100644 --- a/src/passes/GlobalStructInference.cpp +++ b/src/passes/GlobalStructInference.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +// +// GlobalStructInference: Analyze struct usage globally, in particular, structs +// created (perhaps only) in globals. // // Finds types which are only created in assignments to immutable globals. For // such types we can replace a struct.get with a global.get when there is a @@ -47,7 +50,7 @@ // // This also optimizes some related things - reads from structs created in // globals - that benefit from the infrastructure here (see unnesting, below), -// even without this type-based approach. +// even without this type-based approach, and even in open world. // // TODO: Only do the case with a select when shrinkLevel == 0? // @@ -81,6 +84,9 @@ struct GlobalStructInference : public Pass { // // We will remove unoptimizable types from here, so in practice, if a type is // optimizable it will have an entry here, and not if not. + // + // This is filled in when in closed world. In open world, we cannot do such + // type-based inference, and this remains empty. std::unordered_map> typeGlobals; void run(Module* module) override { @@ -88,10 +94,14 @@ struct GlobalStructInference : public Pass { return; } - if (!getPassOptions().closedWorld) { - Fatal() << "GSI requires --closed-world"; + if (getPassOptions().closedWorld) { + analyzeClosedWorld(module); } + optimize(module); + } + + void analyzeClosedWorld(Module* module) { // First, find all the information we need. We need to know which struct // types are created in functions, because we will not be able to optimize // those. @@ -213,7 +223,9 @@ struct GlobalStructInference : public Pass { for (auto& [type, globals] : typeGlobals) { std::sort(globals.begin(), globals.end()); } + } + void optimize(Module* module) { // We are looking for the case where we can pick between two values using a // single comparison. More than two values, or more than a single // comparison, lead to tradeoffs that may not be worth it. diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 8c8ba8d33d0..56a07ca2d44 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -769,7 +769,9 @@ void PassRunner::addDefaultGlobalOptimizationPrePasses() { } else { addIfNoDWARFIssues("cfp"); } - addIfNoDWARFIssues("gsi"); + } + addIfNoDWARFIssues("gsi"); + if (options.closedWorld) { addIfNoDWARFIssues("abstract-type-refining"); addIfNoDWARFIssues("unsubtyping"); } diff --git a/test/lit/passes/gsi-nontype.wast b/test/lit/passes/gsi-nontype.wast index 4d0c158adb0..899fd04fa5a 100644 --- a/test/lit/passes/gsi-nontype.wast +++ b/test/lit/passes/gsi-nontype.wast @@ -1,7 +1,9 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --gsi -all --closed-world -S -o - | filecheck %s -;; Non-type-based optimizations in --gsi +;; RUN: foreach %s %t wasm-opt --gsi -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --gsi --closed-world -all -S -o - | filecheck %s + +;; Non-type-based optimizations in --gsi, which work in open world too. ;; Create an immutable vtable in an immutable global, which we can optimize ;; with. @@ -9,6 +11,7 @@ ;; CHECK: (type $vtable (struct (field funcref))) (type $vtable (struct funcref)) + ;; CHECK: (type $1 (func)) ;; CHECK: (import "a" "b" (global $imported funcref)) @@ -43,6 +46,7 @@ ;; CHECK: (type $vtable (struct (field funcref))) (type $vtable (struct funcref)) + ;; CHECK: (type $1 (func)) ;; CHECK: (import "a" "b" (global $imported funcref)) @@ -79,6 +83,7 @@ ;; CHECK: (type $vtable (struct (field funcref))) (type $vtable (struct funcref)) + ;; CHECK: (type $1 (func)) ;; CHECK: (import "a" "b" (global $imported funcref)) @@ -109,11 +114,13 @@ ;; CHECK: (type $table (struct (field anyref))) (type $table (struct anyref)) + ;; CHECK: (type $1 (func)) ;; CHECK: (import "a" "b" (global $imported funcref)) (import "a" "b" (global $imported funcref)) + ;; CHECK: (global $table.unnested.0 (ref (exact $table)) (struct.new_default $table)) ;; CHECK: (global $table (ref $table) (struct.new $table @@ -149,6 +156,7 @@ (type $desc (sub (describes $struct (struct)))) ) + ;; CHECK: (type $2 (func)) ;; CHECK: (import "a" "b" (global $imported (ref (exact $desc)))) @@ -183,6 +191,7 @@ ;; CHECK: (type $vtable (struct (field funcref))) (type $vtable (struct funcref)) + ;; CHECK: (type $1 (func)) ;; CHECK: (import "a" "b" (global $imported (ref $vtable))) @@ -209,6 +218,7 @@ ;; CHECK: (type $A (struct (field i8))) (type $A (struct (field i8))) + ;; CHECK: (type $1 (func)) ;; CHECK: (global $global (ref $A) (struct.new $A @@ -246,6 +256,7 @@ (type $A.desc (sub (describes $A (struct)))) ) + ;; CHECK: (type $2 (func (result (ref $A.desc)))) ;; CHECK: (global $global (ref $A) (struct.new_default $A @@ -279,6 +290,8 @@ (type $inner (sub (struct (field (ref $func))))) ) + + ;; CHECK: (type $3 (func (result anyref))) ;; CHECK: (global $global.unnested.0 (ref (exact $inner)) (struct.new $inner diff --git a/test/unit/test_passes.py b/test/unit/test_passes.py index 838f1d2f73f..a8a87c8819b 100644 --- a/test/unit/test_passes.py +++ b/test/unit/test_passes.py @@ -36,7 +36,6 @@ def test_O2(self): 'signature-refining', 'gto', 'cfp', - 'gsi', ] for pass_ in CLOSED_WORLD_PASSES: if closed_world: @@ -44,6 +43,13 @@ def test_O2(self): else: self.assertNotIn(pass_, passes) + # some passes run in open world too + OPEN_WORLD_PASSES = [ + 'gsi', + ] + for pass_ in OPEN_WORLD_PASSES: + self.assertIn(pass_, passes) + def test_O3_O1(self): # When we run something like -O3 -O1 we should run -O3 followed by -O1 # (and not -O1 -O1, which would be the case if the last commandline