diff --git a/src/pass.h b/src/pass.h index 36d49f7b74a..973773c494b 100644 --- a/src/pass.h +++ b/src/pass.h @@ -226,6 +226,8 @@ struct PassOptions { // Arbitrary string arguments from the commandline, which we forward to // passes. std::unordered_map arguments; + // Passes to skip and not run. + std::unordered_set passesToSkip; // Effect info computed for functions. One pass can generate this and then // other passes later can benefit from it. It is up to the sequence of passes @@ -386,6 +388,9 @@ struct PassRunner { // Whether this pass runner has run. A pass runner should only be run once. bool ran = false; + // Passes in |options.passesToSkip| that we have seen and skipped. + std::unordered_set skippedPasses; + void runPass(Pass* pass); void runPassOnFunction(Pass* pass, Function* func); diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 373322a51a8..593023c7cc4 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -692,6 +692,9 @@ void PassRunner::run() { assert(!ran); ran = true; + // As we run passes, we'll notice which we skip. + skippedPasses.clear(); + 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 @@ -812,6 +815,16 @@ void PassRunner::run() { } flush(); } + + // All the passes the user requested to skip should have been seen, and + // skipped. If not, the user may have had a typo in the name of a pass to + // skip, and we will warn. + for (auto pass : options.passesToSkip) { + if (!skippedPasses.count(pass)) { + std::cerr << "warning: --" << pass << " was requested to be skipped, " + << "but it was not found in the passes that were run.\n"; + } + } } void PassRunner::runOnFunction(Function* func) { @@ -930,6 +943,13 @@ struct AfterEffectModuleChecker { }; void PassRunner::runPass(Pass* pass) { + assert(!pass->isFunctionParallel()); + + if (options.passesToSkip.count(pass->name)) { + skippedPasses.insert(pass->name); + return; + } + std::unique_ptr checker; if (getPassDebug()) { checker = std::unique_ptr( @@ -949,6 +969,11 @@ void PassRunner::runPass(Pass* pass) { void PassRunner::runPassOnFunction(Pass* pass, Function* func) { assert(pass->isFunctionParallel()); + if (options.passesToSkip.count(pass->name)) { + skippedPasses.insert(pass->name); + return; + } + auto passDebug = getPassDebug(); // Add extra validation logic in pass-debug mode 2. The main logic in diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h index 698d3305e1f..c87530536c9 100644 --- a/src/tools/optimization-options.h +++ b/src/tools/optimization-options.h @@ -277,6 +277,14 @@ struct OptimizationOptions : public ToolOptions { Options::Arguments::Zero, [this](Options*, const std::string&) { passOptions.zeroFilledMemory = true; + }) + .add("--skip-pass", + "-sp", + "Skip a pass (do not run it)", + OptimizationOptionsCategory, + Options::Arguments::One, + [this](Options*, const std::string& pass) { + passOptions.passesToSkip.insert(pass); }); // add passes in registry diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test index 8e04a756caa..186382fd40d 100644 --- a/test/lit/help/wasm-opt.test +++ b/test/lit/help/wasm-opt.test @@ -567,6 +567,8 @@ ;; CHECK-NEXT: --zero-filled-memory,-uim Assume that an imported memory ;; CHECK-NEXT: will be zero-initialized ;; CHECK-NEXT: +;; CHECK-NEXT: --skip-pass,-sp Skip a pass (do not run it) +;; CHECK-NEXT: ;; CHECK-NEXT: ;; CHECK-NEXT: Tool options: ;; CHECK-NEXT: ------------- diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test index e023c2af571..5015040274a 100644 --- a/test/lit/help/wasm2js.test +++ b/test/lit/help/wasm2js.test @@ -526,6 +526,8 @@ ;; CHECK-NEXT: --zero-filled-memory,-uim Assume that an imported memory ;; CHECK-NEXT: will be zero-initialized ;; CHECK-NEXT: +;; CHECK-NEXT: --skip-pass,-sp Skip a pass (do not run it) +;; CHECK-NEXT: ;; CHECK-NEXT: ;; CHECK-NEXT: Tool options: ;; CHECK-NEXT: ------------- diff --git a/test/lit/passes/O1_skip.wast b/test/lit/passes/O1_skip.wast new file mode 100644 index 00000000000..c57d052b4c0 --- /dev/null +++ b/test/lit/passes/O1_skip.wast @@ -0,0 +1,70 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up. + +;; RUN: foreach %s %t wasm-opt -O2 --coalesce-locals --skip-pass=coalesce-locals -S -o - | filecheck %s + +;; We should skip coalese-locals even though it is run in -O2 and also we ask to +;; run it directly: the skip instruction overrides everything else. + +(module + ;; CHECK: (type $i32_i32_=>_none (func (param i32 i32))) + + ;; CHECK: (type $i32_=>_none (func (param i32))) + + ;; CHECK: (import "a" "b" (func $log (param i32 i32))) + (import "a" "b" (func $log (param i32 i32))) + + (func "foo" (param $p i32) + ;; The locals $x and $y can be coalesced into a single local, but as we do not + ;; run that pass, they will not be. Other minor optimizations will occur here, + ;; such as using a tee. + (local $x i32) + (local $y i32) + + (local.set $x + (i32.add + (local.get $p) + (i32.const 1) + ) + ) + (call $log + (local.get $x) + (local.get $x) + ) + + (local.set $y + (i32.add + (local.get $p) + (i32.const 1) + ) + ) + (call $log + (local.get $y) + (local.get $y) + ) + ) +) +;; CHECK: (export "foo" (func $0)) + +;; CHECK: (func $0 (; has Stack IR ;) (param $p i32) +;; CHECK-NEXT: (local $x i32) +;; CHECK-NEXT: (local $y i32) +;; CHECK-NEXT: (call $log +;; CHECK-NEXT: (local.tee $x +;; CHECK-NEXT: (i32.add +;; CHECK-NEXT: (local.get $p) +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $log +;; CHECK-NEXT: (local.tee $y +;; CHECK-NEXT: (i32.add +;; CHECK-NEXT: (local.get $p) +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.get $y) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) diff --git a/test/lit/passes/skip-missing.wast b/test/lit/passes/skip-missing.wast new file mode 100644 index 00000000000..02778dab092 --- /dev/null +++ b/test/lit/passes/skip-missing.wast @@ -0,0 +1,8 @@ +;; We should warn on a pass called "waka" not having been run and skipped. + +;; RUN: wasm-opt %s -O1 --skip-pass=waka 2>&1 | filecheck %s + +;; CHECK: warning: --waka was requested to be skipped, but it was not found in the passes that were run. + +(module +)