summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-02-08 10:38:51 -0800
committerGitHub <noreply@github.com>2024-02-08 10:38:51 -0800
commit6e1e53f093a92177405d59a6733bfe3e57a5b877 (patch)
tree14bff269d7af7b5cf3a86b8179152d09df025976
parentf172920b90f9745494c2efeb1696468fa26be506 (diff)
downloadbinaryen-6e1e53f093a92177405d59a6733bfe3e57a5b877.tar.gz
binaryen-6e1e53f093a92177405d59a6733bfe3e57a5b877.tar.bz2
binaryen-6e1e53f093a92177405d59a6733bfe3e57a5b877.zip
Add a pass to propagate global constants to other globals (#6287)
SimplifyGlobals already does this, so this is a subset of that pass, and does not add anything new. It is useful for testing, however. In particular it allows testing that we propagate subsequent globals in a single pass, that is if one global reads from another and becomes constant, then it can be propagated as well. SimplifyGlobals runs multiple passes so this always worked, but with this pass we can test that we do it efficiently in one pass. This will also be useful for comparing stringref to imported strings, as it allows gathered strings to be propagated to other globals (possible with stringref, but not imported strings) but not anywhere else (which might have downsides as it could lead to more allocations). Also add an additional test for simplify-globals that we do not get confused by an unoptimizable global.get in the middle (see last part).
-rw-r--r--src/passes/SimplifyGlobals.cpp19
-rw-r--r--src/passes/pass.cpp3
-rw-r--r--src/passes/passes.h1
-rw-r--r--test/lit/help/wasm-opt.test3
-rw-r--r--test/lit/help/wasm2js.test3
-rw-r--r--test/lit/passes/propagate-globals-globally.wast106
-rw-r--r--test/lit/passes/simplify-globals-nested.wast9
7 files changed, 140 insertions, 4 deletions
diff --git a/src/passes/SimplifyGlobals.cpp b/src/passes/SimplifyGlobals.cpp
index 323ebd6a7..d4a6e1f3b 100644
--- a/src/passes/SimplifyGlobals.cpp
+++ b/src/passes/SimplifyGlobals.cpp
@@ -673,11 +673,12 @@ struct SimplifyGlobals : public Pass {
// go, as well as applying them where possible.
for (auto& global : module->globals) {
if (!global->imported()) {
+ // Apply globals to this value, which may turn it into a constant we can
+ // further propagate, or it may already have been one.
+ applyGlobals(global->init);
if (Properties::isConstantExpression(global->init)) {
constantGlobals[global->name] =
getLiteralsFromConstExpression(global->init);
- } else {
- applyGlobals(global->init);
}
}
}
@@ -762,10 +763,24 @@ struct SimplifyGlobals : public Pass {
}
};
+// A pass mainly useful for testing that only performs the operation to
+// propagate constant values between globals.
+struct PropagateGlobalsGlobally : public SimplifyGlobals {
+ void run(Module* module_) override {
+ module = module_;
+
+ propagateConstantsToGlobals();
+ }
+};
+
Pass* createSimplifyGlobalsPass() { return new SimplifyGlobals(false); }
Pass* createSimplifyGlobalsOptimizingPass() {
return new SimplifyGlobals(true);
}
+Pass* createPropagateGlobalsGloballyPass() {
+ return new PropagateGlobalsGlobally();
+}
+
} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index c00a5e706..1fe81cbfc 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -369,6 +369,9 @@ void PassRegistry::registerPasses() {
registerPass("print-stack-ir",
"print out Stack IR (useful for internal debugging)",
createPrintStackIRPass);
+ registerPass("propagate-globals-globally",
+ "propagate global values to other globals (useful for tests)",
+ createPropagateGlobalsGloballyPass);
registerPass("remove-non-js-ops",
"removes operations incompatible with js",
createRemoveNonJSOpsPass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 3f8b3fe6b..c3ab7773f 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -123,6 +123,7 @@ Pass* createPrintCallGraphPass();
Pass* createPrintFeaturesPass();
Pass* createPrintFunctionMapPass();
Pass* createPrintStackIRPass();
+Pass* createPropagateGlobalsGloballyPass();
Pass* createRemoveNonJSOpsPass();
Pass* createRemoveImportsPass();
Pass* createRemoveMemoryPass();
diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test
index 88fc2448a..be5924cf7 100644
--- a/test/lit/help/wasm-opt.test
+++ b/test/lit/help/wasm-opt.test
@@ -374,6 +374,9 @@
;; CHECK-NEXT: --print-stack-ir print out Stack IR (useful for
;; CHECK-NEXT: internal debugging)
;; CHECK-NEXT:
+;; CHECK-NEXT: --propagate-globals-globally propagate global values to other
+;; CHECK-NEXT: globals (useful for tests)
+;; CHECK-NEXT:
;; CHECK-NEXT: --remove-imports removes imports and replaces
;; CHECK-NEXT: them with nops
;; CHECK-NEXT:
diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test
index 43243b99a..aadefe8fc 100644
--- a/test/lit/help/wasm2js.test
+++ b/test/lit/help/wasm2js.test
@@ -324,6 +324,9 @@
;; CHECK-NEXT: --print-stack-ir print out Stack IR (useful for
;; CHECK-NEXT: internal debugging)
;; CHECK-NEXT:
+;; CHECK-NEXT: --propagate-globals-globally propagate global values to other
+;; CHECK-NEXT: globals (useful for tests)
+;; CHECK-NEXT:
;; CHECK-NEXT: --remove-imports removes imports and replaces
;; CHECK-NEXT: them with nops
;; CHECK-NEXT:
diff --git a/test/lit/passes/propagate-globals-globally.wast b/test/lit/passes/propagate-globals-globally.wast
new file mode 100644
index 000000000..36d875b17
--- /dev/null
+++ b/test/lit/passes/propagate-globals-globally.wast
@@ -0,0 +1,106 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; RUN: foreach %s %t wasm-opt --propagate-globals-globally -all -S -o - | filecheck %s
+;; RUN: foreach %s %t wasm-opt --simplify-globals -all -S -o - | filecheck %s --check-prefix SIMGB
+
+;; Check that propagate-globals-globally propagates constants globally but not
+;; to code. Also run simplify-globals for comparison, which does do that.
+
+(module
+ ;; CHECK: (type $struct (struct (field stringref) (field stringref)))
+ ;; SIMGB: (type $struct (struct (field stringref) (field stringref)))
+ (type $struct (struct stringref stringref))
+
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (global $A i32 (i32.const 42))
+ ;; SIMGB: (type $1 (func))
+
+ ;; SIMGB: (global $A i32 (i32.const 42))
+ (global $A i32 (i32.const 42))
+
+ ;; CHECK: (global $B i32 (i32.const 42))
+ ;; SIMGB: (global $B i32 (i32.const 42))
+ (global $B i32 (global.get $A))
+
+ ;; CHECK: (global $C i32 (i32.add
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+ ;; SIMGB: (global $C i32 (i32.add
+ ;; SIMGB-NEXT: (i32.const 42)
+ ;; SIMGB-NEXT: (i32.const 42)
+ ;; SIMGB-NEXT: ))
+ (global $C i32 (i32.add
+ ;; Both of these can be optimized, including $B which reads from $A.
+ (global.get $B)
+ (global.get $A)
+ ))
+
+ ;; CHECK: (global $D (ref string) (string.const "foo"))
+ ;; SIMGB: (global $D (ref string) (string.const "foo"))
+ (global $D (ref string) (string.const "foo"))
+
+ ;; CHECK: (global $E (ref string) (string.const "bar"))
+ ;; SIMGB: (global $E (ref string) (string.const "bar"))
+ (global $E (ref string) (string.const "bar"))
+
+ ;; CHECK: (global $G (ref $struct) (struct.new $struct
+ ;; CHECK-NEXT: (string.const "foo")
+ ;; CHECK-NEXT: (string.const "bar")
+ ;; CHECK-NEXT: ))
+ ;; SIMGB: (global $G (ref $struct) (struct.new $struct
+ ;; SIMGB-NEXT: (string.const "foo")
+ ;; SIMGB-NEXT: (string.const "bar")
+ ;; SIMGB-NEXT: ))
+ (global $G (ref $struct) (struct.new $struct
+ (global.get $D)
+ (global.get $E)
+ ))
+
+ ;; CHECK: (func $test (type $1)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (global.get $A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (global.get $B)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (global.get $C)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (global.get $D)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; SIMGB: (func $test (type $1)
+ ;; SIMGB-NEXT: (drop
+ ;; SIMGB-NEXT: (i32.const 42)
+ ;; SIMGB-NEXT: )
+ ;; SIMGB-NEXT: (drop
+ ;; SIMGB-NEXT: (i32.const 42)
+ ;; SIMGB-NEXT: )
+ ;; SIMGB-NEXT: (drop
+ ;; SIMGB-NEXT: (global.get $C)
+ ;; SIMGB-NEXT: )
+ ;; SIMGB-NEXT: (drop
+ ;; SIMGB-NEXT: (string.const "foo")
+ ;; SIMGB-NEXT: )
+ ;; SIMGB-NEXT: )
+ (func $test
+ ;; We should not change anything here: this pass propagates globals
+ ;; *globally*, and not to functions. (but simplify-globals does, except for
+ ;; $C which is not constant)
+ (drop
+ (global.get $A)
+ )
+ (drop
+ (global.get $B)
+ )
+ (drop
+ (global.get $C)
+ )
+ (drop
+ (global.get $D)
+ )
+ )
+)
diff --git a/test/lit/passes/simplify-globals-nested.wast b/test/lit/passes/simplify-globals-nested.wast
index 925d71161..48116e198 100644
--- a/test/lit/passes/simplify-globals-nested.wast
+++ b/test/lit/passes/simplify-globals-nested.wast
@@ -6,8 +6,11 @@
;; Test that we propagate globals into nested children of other globals.
(module
- ;; CHECK: (type $struct (struct (field i32) (field i32)))
- (type $struct (struct i32 i32))
+ ;; CHECK: (type $struct (struct (field i32) (field i32) (field i32)))
+ (type $struct (struct i32 i32 i32))
+
+ ;; CHECK: (import "x" "y" (global $no i32))
+ (import "x" "y" (global $no i32))
;; CHECK: (global $a i32 (i32.const 42))
(global $a i32 (i32.const 42))
@@ -17,10 +20,12 @@
;; CHECK: (global $struct (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: (global.get $no)
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $struct (ref $struct) (struct.new $struct
(global.get $a)
+ (global.get $no) ;; the middle item cannot be optimized
(global.get $b)
))
)