summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pass.h11
-rw-r--r--src/passes/pass.cpp5
-rw-r--r--src/tools/optimization-options.h44
-rw-r--r--test/lit/passes/global-effects-O.wast198
4 files changed, 229 insertions, 29 deletions
diff --git a/src/pass.h b/src/pass.h
index 83f53c23a..2c2fa0619 100644
--- a/src/pass.h
+++ b/src/pass.h
@@ -235,8 +235,9 @@ struct PassOptions {
// other passes later can benefit from it. It is up to the sequence of passes
// to update or discard this when necessary - in particular, when new effects
// are added to a function this must be changed or we may optimize
- // incorrectly (however, it is extremely rare for a pass to *add* effects;
- // passes normally only remove effects).
+ // incorrectly. However, it is extremely rare for a pass to *add* effects;
+ // passes normally only remove effects. Passes that do add effects must set
+ // addsEffects() so the pass runner is aware of them.
std::shared_ptr<FuncEffectsMap> funcEffectsMap;
// -Os is our default
@@ -318,6 +319,9 @@ struct PassRunner {
// Add a pass given an instance.
void add(std::unique_ptr<Pass> pass) { doAdd(std::move(pass)); }
+ // Clears away all passes that have been added.
+ void clear();
+
// Adds the pass if there are no DWARF-related issues. There is an issue if
// there is DWARF and if the pass does not support DWARF (as defined by the
// pass returning true from invalidatesDWARF); otherwise, if there is no
@@ -387,9 +391,6 @@ private:
// yet) have removed DWARF.
bool addedPassesRemovedDWARF = false;
- // Whether this pass runner has run. A pass runner should only be run once.
- bool ran = false;
-
void runPass(Pass* pass);
void runPassOnFunction(Pass* pass, Function* func);
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index d7391a390..62b3683fa 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -739,9 +739,6 @@ static void dumpWasm(Name name, Module* wasm) {
}
void PassRunner::run() {
- assert(!ran);
- ran = true;
-
static const int passDebug = getPassDebug();
// Emit logging information when asked for. At passDebug level 1+ we log
// the main passes, while in 2 we also log nested ones. Note that for
@@ -885,6 +882,8 @@ void PassRunner::doAdd(std::unique_ptr<Pass> pass) {
passes.emplace_back(std::move(pass));
}
+void PassRunner::clear() { passes.clear(); }
+
// Checks that the state is valid before and after a
// pass runs on a function. We run these extra checks when
// pass-debug mode is enabled.
diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h
index 045542ac2..0a47d9f70 100644
--- a/src/tools/optimization-options.h
+++ b/src/tools/optimization-options.h
@@ -328,44 +328,46 @@ struct OptimizationOptions : public ToolOptions {
bool runningPasses() { return passes.size() > 0; }
void runPasses(Module& wasm) {
- std::unique_ptr<PassRunner> passRunner;
+ PassRunner passRunner(&wasm, passOptions);
+ if (debug) {
+ passRunner.setDebug(true);
+ }
// Flush anything in the current pass runner, and then reset it to a fresh
// state so it is ready for new things.
- auto flushAndReset = [&]() {
- if (passRunner) {
- passRunner->run();
- }
- passRunner = std::make_unique<PassRunner>(&wasm, passOptions);
- if (debug) {
- passRunner->setDebug(true);
- }
+ auto flush = [&]() {
+ passRunner.run();
+ passRunner.clear();
};
- flushAndReset();
-
for (auto& pass : passes) {
if (pass.name == DEFAULT_OPT_PASSES) {
// This is something like -O3 or -Oz. We must run this now, in order to
// set the proper opt and shrink levels. To do that, first reset the
// runner so that anything already queued is run (since we can only run
// after those things).
- flushAndReset();
+ flush();
// -O3/-Oz etc. always set their own optimize/shrinkLevels.
assert(pass.optimizeLevel);
assert(pass.shrinkLevel);
- passRunner->options.optimizeLevel = *pass.optimizeLevel;
- passRunner->options.shrinkLevel = *pass.shrinkLevel;
- // Run our optimizations now, and reset the runner so that the default
- // pass options are used later (and not the temporary optimize/
- // shrinkLevels we just set).
- passRunner->addDefaultOptimizationPasses();
- flushAndReset();
+ // Temporarily override the default levels.
+ assert(passRunner.options.optimizeLevel == passOptions.optimizeLevel);
+ assert(passRunner.options.shrinkLevel == passOptions.shrinkLevel);
+ passRunner.options.optimizeLevel = *pass.optimizeLevel;
+ passRunner.options.shrinkLevel = *pass.shrinkLevel;
+
+ // Run our optimizations now with the custom levels.
+ passRunner.addDefaultOptimizationPasses();
+ flush();
+
+ // Restore the default optimize/shrinkLevels.
+ passRunner.options.optimizeLevel = passOptions.optimizeLevel;
+ passRunner.options.shrinkLevel = passOptions.shrinkLevel;
} else {
// This is a normal pass. Add it to the queue for execution.
- passRunner->add(pass.name);
+ passRunner.add(pass.name);
// Normal passes do not set their own optimize/shrinkLevels.
assert(!pass.optimizeLevel);
@@ -373,7 +375,7 @@ struct OptimizationOptions : public ToolOptions {
}
}
- flushAndReset();
+ flush();
}
};
diff --git a/test/lit/passes/global-effects-O.wast b/test/lit/passes/global-effects-O.wast
new file mode 100644
index 000000000..c24159eac
--- /dev/null
+++ b/test/lit/passes/global-effects-O.wast
@@ -0,0 +1,198 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; RUN: foreach %s %t wasm-opt -all -S -o - --generate-global-effects | filecheck %s --check-prefix CHECK_0
+;; RUN: foreach %s %t wasm-opt -all -S -o - --generate-global-effects -O1 | filecheck %s --check-prefix CHECK_1
+;; RUN: foreach %s %t wasm-opt -all -S -o - --generate-global-effects -O3 | filecheck %s --check-prefix CHECK_3
+;; RUN: foreach %s %t wasm-opt -all -S -o - --generate-global-effects -Os | filecheck %s --check-prefix CHECK_s
+;; RUN: foreach %s %t wasm-opt -all -S -o - --generate-global-effects -O | filecheck %s --check-prefix CHECK_O
+
+;; Test that global effects benefit -O1 and related modes.
+
+(module
+ ;; CHECK_0: (type $0 (func))
+
+ ;; CHECK_0: (type $1 (func (result i32)))
+
+ ;; CHECK_0: (export "main" (func $main))
+ ;; CHECK_1: (type $0 (func))
+
+ ;; CHECK_1: (type $1 (func (result i32)))
+
+ ;; CHECK_1: (export "main" (func $main))
+ ;; CHECK_3: (type $0 (func))
+
+ ;; CHECK_3: (type $1 (func (result i32)))
+
+ ;; CHECK_3: (export "main" (func $main))
+ ;; CHECK_s: (type $0 (func))
+
+ ;; CHECK_s: (type $1 (func (result i32)))
+
+ ;; CHECK_s: (export "main" (func $main))
+ ;; CHECK_O: (type $0 (func))
+
+ ;; CHECK_O: (type $1 (func (result i32)))
+
+ ;; CHECK_O: (export "main" (func $main))
+ (export "main" (func $main))
+
+ ;; CHECK_0: (export "pointless-work" (func $pointless-work))
+ ;; CHECK_1: (export "pointless-work" (func $pointless-work))
+ ;; CHECK_3: (export "pointless-work" (func $pointless-work))
+ ;; CHECK_s: (export "pointless-work" (func $pointless-work))
+ ;; CHECK_O: (export "pointless-work" (func $pointless-work))
+ (export "pointless-work" (func $pointless-work))
+
+ ;; CHECK_0: (func $main (type $0)
+ ;; CHECK_0-NEXT: (if
+ ;; CHECK_0-NEXT: (call $pointless-work)
+ ;; CHECK_0-NEXT: (then
+ ;; CHECK_0-NEXT: (drop
+ ;; CHECK_0-NEXT: (call $pointless-work)
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_1: (func $main (type $0)
+ ;; CHECK_1-NEXT: (nop)
+ ;; CHECK_1-NEXT: )
+ ;; CHECK_3: (func $main (type $0) (; has Stack IR ;)
+ ;; CHECK_3-NEXT: (nop)
+ ;; CHECK_3-NEXT: )
+ ;; CHECK_s: (func $main (type $0) (; has Stack IR ;)
+ ;; CHECK_s-NEXT: (nop)
+ ;; CHECK_s-NEXT: )
+ ;; CHECK_O: (func $main (type $0) (; has Stack IR ;)
+ ;; CHECK_O-NEXT: (nop)
+ ;; CHECK_O-NEXT: )
+ (func $main
+ ;; This calls a function that does pointless work. After generating global
+ ;; effects we can see that it is pointless and remove this entire if (except
+ ;; for -O0).
+ (if
+ (call $pointless-work)
+ (then
+ (drop
+ (call $pointless-work)
+ )
+ )
+ )
+ )
+
+ ;; CHECK_0: (func $pointless-work (type $1) (result i32)
+ ;; CHECK_0-NEXT: (local $x i32)
+ ;; CHECK_0-NEXT: (loop $loop
+ ;; CHECK_0-NEXT: (local.set $x
+ ;; CHECK_0-NEXT: (i32.add
+ ;; CHECK_0-NEXT: (local.get $x)
+ ;; CHECK_0-NEXT: (i32.const 1)
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_0-NEXT: (if
+ ;; CHECK_0-NEXT: (i32.ge_u
+ ;; CHECK_0-NEXT: (local.get $x)
+ ;; CHECK_0-NEXT: (i32.const 12345678)
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_0-NEXT: (then
+ ;; CHECK_0-NEXT: (return
+ ;; CHECK_0-NEXT: (local.get $x)
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_0-NEXT: (br $loop)
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_0-NEXT: )
+ ;; CHECK_1: (func $pointless-work (type $1) (result i32)
+ ;; CHECK_1-NEXT: (local $0 i32)
+ ;; CHECK_1-NEXT: (loop $loop (result i32)
+ ;; CHECK_1-NEXT: (br_if $loop
+ ;; CHECK_1-NEXT: (i32.lt_u
+ ;; CHECK_1-NEXT: (local.tee $0
+ ;; CHECK_1-NEXT: (i32.add
+ ;; CHECK_1-NEXT: (local.get $0)
+ ;; CHECK_1-NEXT: (i32.const 1)
+ ;; CHECK_1-NEXT: )
+ ;; CHECK_1-NEXT: )
+ ;; CHECK_1-NEXT: (i32.const 12345678)
+ ;; CHECK_1-NEXT: )
+ ;; CHECK_1-NEXT: )
+ ;; CHECK_1-NEXT: (local.get $0)
+ ;; CHECK_1-NEXT: )
+ ;; CHECK_1-NEXT: )
+ ;; CHECK_3: (func $pointless-work (type $1) (; has Stack IR ;) (result i32)
+ ;; CHECK_3-NEXT: (local $0 i32)
+ ;; CHECK_3-NEXT: (loop $loop (result i32)
+ ;; CHECK_3-NEXT: (br_if $loop
+ ;; CHECK_3-NEXT: (i32.lt_u
+ ;; CHECK_3-NEXT: (local.tee $0
+ ;; CHECK_3-NEXT: (i32.add
+ ;; CHECK_3-NEXT: (local.get $0)
+ ;; CHECK_3-NEXT: (i32.const 1)
+ ;; CHECK_3-NEXT: )
+ ;; CHECK_3-NEXT: )
+ ;; CHECK_3-NEXT: (i32.const 12345678)
+ ;; CHECK_3-NEXT: )
+ ;; CHECK_3-NEXT: )
+ ;; CHECK_3-NEXT: (local.get $0)
+ ;; CHECK_3-NEXT: )
+ ;; CHECK_3-NEXT: )
+ ;; CHECK_s: (func $pointless-work (type $1) (; has Stack IR ;) (result i32)
+ ;; CHECK_s-NEXT: (local $0 i32)
+ ;; CHECK_s-NEXT: (loop $loop (result i32)
+ ;; CHECK_s-NEXT: (br_if $loop
+ ;; CHECK_s-NEXT: (i32.lt_u
+ ;; CHECK_s-NEXT: (local.tee $0
+ ;; CHECK_s-NEXT: (i32.add
+ ;; CHECK_s-NEXT: (local.get $0)
+ ;; CHECK_s-NEXT: (i32.const 1)
+ ;; CHECK_s-NEXT: )
+ ;; CHECK_s-NEXT: )
+ ;; CHECK_s-NEXT: (i32.const 12345678)
+ ;; CHECK_s-NEXT: )
+ ;; CHECK_s-NEXT: )
+ ;; CHECK_s-NEXT: (local.get $0)
+ ;; CHECK_s-NEXT: )
+ ;; CHECK_s-NEXT: )
+ ;; CHECK_O: (func $pointless-work (type $1) (; has Stack IR ;) (result i32)
+ ;; CHECK_O-NEXT: (local $0 i32)
+ ;; CHECK_O-NEXT: (loop $loop (result i32)
+ ;; CHECK_O-NEXT: (br_if $loop
+ ;; CHECK_O-NEXT: (i32.lt_u
+ ;; CHECK_O-NEXT: (local.tee $0
+ ;; CHECK_O-NEXT: (i32.add
+ ;; CHECK_O-NEXT: (local.get $0)
+ ;; CHECK_O-NEXT: (i32.const 1)
+ ;; CHECK_O-NEXT: )
+ ;; CHECK_O-NEXT: )
+ ;; CHECK_O-NEXT: (i32.const 12345678)
+ ;; CHECK_O-NEXT: )
+ ;; CHECK_O-NEXT: )
+ ;; CHECK_O-NEXT: (local.get $0)
+ ;; CHECK_O-NEXT: )
+ ;; CHECK_O-NEXT: )
+ (func $pointless-work (result i32)
+ (local $x i32)
+ ;; Some pointless work, with no side effects, that cannot be inlined. (The
+ ;; changes here are not important for this test.)
+ (loop $loop
+ (local.set $x
+ (i32.add
+ (local.get $x)
+ (i32.const 1)
+ )
+ )
+ (if
+ (i32.ge_u
+ (local.get $x)
+ (i32.const 12345678)
+ )
+ (then
+ (return
+ (local.get $x)
+ )
+ )
+ )
+ (br $loop)
+ )
+ )
+)