diff options
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 43 | ||||
-rw-r--r-- | test/lit/passes/asyncify_optimize-level=1.wast | 497 | ||||
-rw-r--r-- | test/lit/passes/remove-unused-brs.wast | 160 |
3 files changed, 444 insertions, 256 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 9861695e0..cbad81247 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -80,6 +80,8 @@ static bool canTurnIfIntoBrIf(Expression* ifCondition, return !EffectAnalyzer(options, wasm, ifCondition).invalidates(value); } +const Index TooCostlyToRunUnconditionally = 7; + // Check if it is not worth it to run code unconditionally. This // assumes we are trying to run two expressions where previously // only one of the two might have executed. We assume here that @@ -92,9 +94,18 @@ static bool tooCostlyToRunUnconditionally(const PassOptions& passOptions, return false; } // Consider the cost of executing all the code unconditionally. - const auto TOO_MUCH = 7; auto total = CostAnalyzer(one).cost + CostAnalyzer(two).cost; - return total >= TOO_MUCH; + return total >= TooCostlyToRunUnconditionally; +} + +// As above, but a single expression that we are considering moving to a place +// where it executes unconditionally. +static bool tooCostlyToRunUnconditionally(const PassOptions& passOptions, + Expression* curr) { + if (passOptions.shrinkLevel) { + return false; + } + return CostAnalyzer(curr).cost >= TooCostlyToRunUnconditionally; } struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { @@ -374,6 +385,34 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { anotherCycle = true; } } + + // if (condition-A) { if (condition-B) .. } + // => + // if (condition-A ? condition-B : 0) { .. } + // + // This replaces an if, which is 3 bytes, with a select plus a zero, which + // is also 3 bytes. The benefit is that the select may be faster, and also + // further optimizations may be possible on the select. + if (auto* child = curr->ifTrue->dynCast<If>()) { + if (child->ifFalse) { + return; + } + // If running the child's condition unconditionally is too expensive, + // give up. + if (tooCostlyToRunUnconditionally(getPassOptions(), child->condition)) { + return; + } + // Of course we can't do this if the inner if's condition has side + // effects, as we would then execute those unconditionally. + if (EffectAnalyzer(getPassOptions(), *getModule(), child->condition) + .hasSideEffects()) { + return; + } + Builder builder(*getModule()); + curr->condition = builder.makeSelect( + child->condition, curr->condition, builder.makeConst(int32_t(0))); + curr->ifTrue = child->ifTrue; + } } // TODO: if-else can be turned into a br_if as well, if one of the sides is // a dead end we handle the case of a returned value to a local.set diff --git a/test/lit/passes/asyncify_optimize-level=1.wast b/test/lit/passes/asyncify_optimize-level=1.wast index 9406691c4..aa5172f26 100644 --- a/test/lit/passes/asyncify_optimize-level=1.wast +++ b/test/lit/passes/asyncify_optimize-level=1.wast @@ -460,54 +460,51 @@ ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (block $__asyncify_unwind (result i32) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.or + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.or - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (select - ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_if $__asyncify_unwind ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (call $import) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (br_if $__asyncify_unwind - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -540,105 +537,105 @@ ;; CHECK: (func $calls-import2-if-else (param $0 i32) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 4) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (block $__asyncify_unwind (result i32) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 4) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $2 - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.or + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (global.get $__asyncify_state) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.or - ;; CHECK-NEXT: (local.tee $1 - ;; CHECK-NEXT: (select - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.eq ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (select - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $import3 + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (call $import3 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (br_if $__asyncify_unwind - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_if $__asyncify_unwind + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.or - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.or + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (select ;; CHECK-NEXT: (i32.eq ;; CHECK-NEXT: (local.get $2) @@ -647,17 +644,17 @@ ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: (global.get $__asyncify_state) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (call $import3 - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (br_if $__asyncify_unwind + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $import3 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_if $__asyncify_unwind + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -733,57 +730,55 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.or - ;; CHECK-NEXT: (local.tee $2 - ;; CHECK-NEXT: (select - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.or + ;; CHECK-NEXT: (local.tee $2 + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.get $__asyncify_state) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.or - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.or + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (select - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $import3 + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (call $import3 - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (br_if $__asyncify_unwind - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_if $__asyncify_unwind + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -827,111 +822,105 @@ ;; CHECK: (func $calls-import2-if-else-oneside2 (param $0 i32) (result i32) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 4) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (block $__asyncify_unwind (result i32) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 4) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $2 - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.or + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (global.get $__asyncify_state) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.or - ;; CHECK-NEXT: (local.tee $1 - ;; CHECK-NEXT: (select - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.eq ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (select - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $import3 + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (call $import3 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (br_if $__asyncify_unwind - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_if $__asyncify_unwind + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.or - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.or + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.get $__asyncify_state) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if diff --git a/test/lit/passes/remove-unused-brs.wast b/test/lit/passes/remove-unused-brs.wast index 9ed319cb8..e5b29d2e6 100644 --- a/test/lit/passes/remove-unused-brs.wast +++ b/test/lit/passes/remove-unused-brs.wast @@ -335,4 +335,164 @@ ) ) ) + + ;; CHECK: (func $if-of-if + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $if-of-if) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-of-if + (local $x i32) + ;; The outer if has side effects in the condition while the inner one does + ;; not, which means we can fold them. + (if + (local.tee $x + (i32.const 1) + ) + (if + (local.get $x) + (call $if-of-if) + ) + ) + ) + + ;; CHECK: (func $if-of-if-but-side-effects + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $if-of-if-but-side-effects) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-of-if-but-side-effects + (local $x i32) + ;; The inner if has side effects in the condition, which prevents this + ;; optimization. + (if + (local.tee $x + (i32.const 1) + ) + (if + (local.tee $x + (i32.const 2) + ) + (call $if-of-if-but-side-effects) + ) + ) + ) + + ;; CHECK: (func $if-of-if-but-too-costly + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $if-of-if-but-too-costly) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-of-if-but-too-costly + (local $x i32) + ;; The inner if's condition has no effects, but it is very costly, so do not + ;; run it unconditionally - leave this unoptimized. + (if + (local.tee $x + (i32.const 1) + ) + (if + (i32.eqz (i32.eqz (i32.eqz (i32.eqz (i32.eqz (i32.eqz (i32.eqz (i32.eqz + (local.get $x) + )))))))) + (call $if-of-if-but-too-costly) + ) + ) + ) + + ;; CHECK: (func $if-of-if-but-inner-else + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (call $if-of-if-but-inner-else) + ;; CHECK-NEXT: (call $if-of-if) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-of-if-but-inner-else + (local $x i32) + ;; The inner if has an else. For now, leave this unoptimized. + (if + (local.tee $x + (i32.const 1) + ) + (if + (local.get $x) + (call $if-of-if-but-inner-else) + (call $if-of-if) + ) + ) + ) + + ;; CHECK: (func $if-of-if-but-outer-else + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (call $if-of-if-but-outer-else) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $if-of-if) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-of-if-but-outer-else + (local $x i32) + ;; The outer if has an else. For now, leave this unoptimized. + (if + (local.tee $x + (i32.const 1) + ) + (if + (local.get $x) + (call $if-of-if-but-outer-else) + ) + (call $if-of-if) + ) + ) ) |