summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pass.h17
-rw-r--r--src/passes/SimplifyGlobals.cpp16
-rw-r--r--test/lit/passes/simplify-globals_func-effects.wast91
3 files changed, 116 insertions, 8 deletions
diff --git a/src/pass.h b/src/pass.h
index f4b15a320..2cb661e72 100644
--- a/src/pass.h
+++ b/src/pass.h
@@ -488,10 +488,19 @@ public:
assert(getPassRunner());
// Parallel pass running is implemented in the PassRunner.
if (isFunctionParallel()) {
- // TODO: We should almost certainly be propagating pass options here, but
- // that is a widespread change, so make sure it doesn't unacceptably
- // regress compile times.
- PassRunner runner(module /*, getPassOptions()*/);
+ // Reduce opt/shrink levels to a maximum of one in nested runners like
+ // these, to balance runtime. We definitely want the full levels in the
+ // main passes we run, but nested pass runners are of secondary
+ // importance.
+ // TODO Investigate the impact of allowing the levels to just pass
+ // through. That seems to cause at least some regression in compile
+ // times in -O3, however, but with careful measurement we may find
+ // the benefits are worth it. For now -O1 is a reasonable compromise
+ // as it has basically linear runtime, unlike -O2 and -O3.
+ auto options = getPassOptions();
+ options.optimizeLevel = std::min(options.optimizeLevel, 1);
+ options.shrinkLevel = std::min(options.shrinkLevel, 1);
+ PassRunner runner(module, options);
runner.setIsNested(true);
runner.add(create());
runner.run();
diff --git a/src/passes/SimplifyGlobals.cpp b/src/passes/SimplifyGlobals.cpp
index 463261d35..f9bccab23 100644
--- a/src/passes/SimplifyGlobals.cpp
+++ b/src/passes/SimplifyGlobals.cpp
@@ -364,12 +364,20 @@ struct ConstantGlobalApplier
}
return;
}
- // Otherwise, invalidate if we need to.
- EffectAnalyzer effects(getPassOptions(), *getModule());
- effects.visit(curr);
- assert(effects.globalsWritten.empty()); // handled above
+
+ // Otherwise, invalidate if we need to. Note that we handled a GlobalSet
+ // earlier, but also need to handle calls. A general call forces us to
+ // forget everything, but in some cases we can do better, if we have a call
+ // and have computed function effects for it.
+ ShallowEffectAnalyzer effects(getPassOptions(), *getModule(), curr);
if (effects.calls) {
+ // Forget everything.
currConstantGlobals.clear();
+ } else {
+ // Forget just the globals written, if any.
+ for (auto writtenGlobal : effects.globalsWritten) {
+ currConstantGlobals.erase(writtenGlobal);
+ }
}
}
diff --git a/test/lit/passes/simplify-globals_func-effects.wast b/test/lit/passes/simplify-globals_func-effects.wast
new file mode 100644
index 000000000..8257c5ce7
--- /dev/null
+++ b/test/lit/passes/simplify-globals_func-effects.wast
@@ -0,0 +1,91 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; RUN: foreach %s %t wasm-opt --generate-global-effects --simplify-globals -S -o - | filecheck %s
+
+;; Compute function effects and then simplify globals. We must handle the case
+;; of an expression that is not a global.set that sets a global - a function
+;; whose effects we've computed to include some sets to globals.
+
+(module
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (global $A (mut i32) (i32.const 10))
+ (global $A (mut i32) (i32.const 10))
+
+ ;; CHECK: (global $B i32 (i32.const 20))
+ (global $B (mut i32) (i32.const 20))
+
+ ;; CHECK: (global $C (mut i32) (i32.const 30))
+ (global $C (mut i32) (i32.const 30))
+
+ ;; CHECK: (func $set
+ ;; CHECK-NEXT: (global.set $A
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $set
+ (global.set $A
+ (i32.const 0)
+ )
+ )
+
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (global.set $A
+ ;; CHECK-NEXT: (i32.const 11)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.set $C
+ ;; CHECK-NEXT: (i32.const 33)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 11)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 33)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $set)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (global.get $A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 33)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ (global.set $A
+ (i32.const 11)
+ )
+ (global.set $C
+ (i32.const 33)
+ )
+ ;; We can infer $A here since we see the write to it, above.
+ (drop
+ (global.get $A)
+ )
+ ;; We can infer $B since we'll prove it is immutable.
+ (drop
+ (global.get $B)
+ )
+ ;; We can infer $C here since we see the write to it, above.
+ (drop
+ (global.get $C)
+ )
+ ;; This call sets $A. After the call we can no longer infer $A, but we can
+ ;; still infer the others.
+ (call $set)
+ (drop
+ (global.get $A)
+ )
+ (drop
+ (global.get $B)
+ )
+ (drop
+ (global.get $C)
+ )
+ )
+)