From a2a8d2a3a067a23c547b51b4544a933f77a1c03c Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 25 Nov 2024 12:05:15 -0800 Subject: Handle unoptimized branches in CodeFolding (#7111) CodeFolding previously did not consider br_on_* instructions at all, so it would happily merge tails even if there were br_on_* branches to the same label with non-matching tails. Fix the bug by making any label targeted by any instruction not explicitly handled by CodeFolding unoptimizable. This will gracefully handle other branching instructions like `resume` and `resume_throw` as well. Folding these branches properly is left as future work. Also rename the test file from code-folding_enable-threads.wast to just code-folding.wast and enable all features instead of just threads. The old name was left over from when the test was originally ported to lit, and the new feature is necessary because the new test uses GC instructions. --- src/passes/CodeFolding.cpp | 22 +- test/lit/passes/code-folding.wast | 523 +++++++++++++++++++++++ test/lit/passes/code-folding_enable-threads.wast | 484 --------------------- 3 files changed, 537 insertions(+), 492 deletions(-) create mode 100644 test/lit/passes/code-folding.wast delete mode 100644 test/lit/passes/code-folding_enable-threads.wast diff --git a/src/passes/CodeFolding.cpp b/src/passes/CodeFolding.cpp index 21527da6b..0cddec4ca 100644 --- a/src/passes/CodeFolding.cpp +++ b/src/passes/CodeFolding.cpp @@ -84,7 +84,9 @@ struct ExpressionMarker void visitExpression(Expression* expr) { marked.insert(expr); } }; -struct CodeFolding : public WalkerPass> { +struct CodeFolding + : public WalkerPass< + ControlFlowWalker>> { bool isFunctionParallel() override { return true; } std::unique_ptr create() override { @@ -138,6 +140,17 @@ struct CodeFolding : public WalkerPass> { // walking + void visitExpression(Expression* curr) { + // For any branching instruction not explicitly handled by this pass, mark + // the labels it branches to unoptimizable. + // TODO: Handle folding br_on* instructions. br_on_null could be folded with + // other kinds of branches and br_on_non_null, br_on_cast, and + // br_on_cast_fail instructions could be folded with other copies of + // themselves. + BranchUtils::operateOnScopeNameUses( + curr, [&](Name label) { unoptimizables.insert(label); }); + } + void visitBreak(Break* curr) { if (curr->condition || curr->value) { unoptimizables.insert(curr->name); @@ -155,13 +168,6 @@ struct CodeFolding : public WalkerPass> { } } - void visitSwitch(Switch* curr) { - for (auto target : curr->targets) { - unoptimizables.insert(target); - } - unoptimizables.insert(curr->default_); - } - void visitUnreachable(Unreachable* curr) { // we can only optimize if we are at the end of the parent block if (!controlFlowStack.empty()) { diff --git a/test/lit/passes/code-folding.wast b/test/lit/passes/code-folding.wast new file mode 100644 index 000000000..358167481 --- /dev/null +++ b/test/lit/passes/code-folding.wast @@ -0,0 +1,523 @@ +;; 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 -all --code-folding -S -o - | filecheck %s + +(module + ;; CHECK: (type $0 (func)) + + ;; CHECK: (type $1 (func (result f32))) + + ;; CHECK: (type $13 (func (param f32))) + (type $13 (func (param f32))) + (table 282 282 funcref) + ;; CHECK: (memory $0 1 1) + (memory $0 1 1) + ;; CHECK: (table $0 282 282 funcref) + + ;; CHECK: (func $0 (type $0) + ;; CHECK-NEXT: (block $label$1 + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (block $label$3 + ;; CHECK-NEXT: (call_indirect $0 (type $13) + ;; CHECK-NEXT: (block $label$4 + ;; CHECK-NEXT: (br $label$3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 105) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $0 + (block $label$1 + (if + (i32.const 1) + (then + (block $label$3 + (call_indirect (type $13) + (block $label$4 (result f32) ;; but this type may change dangerously + (nop) ;; fold this + (br $label$3) + ) + (i32.const 105) + ) + (nop) ;; with this + ) + ) + ) + ) + ) + ;; CHECK: (func $negative-zero (type $1) (result f32) + ;; CHECK-NEXT: (if (result f32) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (block $label$0 (result f32) + ;; CHECK-NEXT: (f32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (block $label$1 (result f32) + ;; CHECK-NEXT: (f32.const -0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $negative-zero (result f32) + (if (result f32) + (i32.const 0) + (then + (block $label$0 (result f32) + (f32.const 0) + ) + ) + (else + (block $label$1 (result f32) + (f32.const -0) + ) + ) + ) + ) + ;; CHECK: (func $negative-zero-b (type $1) (result f32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $label$0 (result f32) + ;; CHECK-NEXT: (f32.const -0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $negative-zero-b (result f32) + (if (result f32) + (i32.const 0) + (then + (block $label$0 (result f32) + (f32.const -0) + ) + ) + (else + (block $label$1 (result f32) + (f32.const -0) + ) + ) + ) + ) + ;; CHECK: (func $negative-zero-c (type $1) (result f32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $label$0 (result f32) + ;; CHECK-NEXT: (f32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $negative-zero-c (result f32) + (if (result f32) + (i32.const 0) + (then + (block $label$0 (result f32) + (f32.const 0) + ) + ) + (else + (block $label$1 (result f32) + (f32.const 0) + ) + ) + ) + ) + ;; CHECK: (func $break-target-outside-of-return-merged-code (type $0) + ;; CHECK-NEXT: (block $label$A + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $label$B + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (br_table $label$A $label$B + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (block $label$C + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (br_table $label$A $label$C + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $break-target-outside-of-return-merged-code + (block $label$A + (if + (unreachable) + (then + (block + (block + (block $label$B + (if + (unreachable) + (then + (br_table $label$A $label$B + (unreachable) + ) + ) + ) + ) + (return) + ) + ) + ) + (else + (block + (block $label$C + (if + (unreachable) + (then + (br_table $label$A $label$C ;; this all looks mergeable, but $label$A is outside + (unreachable) + ) + ) + ) + ) + (return) + ) + ) + ) + ) + ) + ;; CHECK: (func $break-target-inside-all-good (type $0) + ;; CHECK-NEXT: (block $folding-inner0 + ;; CHECK-NEXT: (block $label$A + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (br $folding-inner0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (br $folding-inner0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $label$B + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (br_table $label$B $label$B + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + (func $break-target-inside-all-good + (block $label$A + (if + (unreachable) + (then + (block + (block + (block $label$B + (if + (unreachable) + (then + (br_table $label$B $label$B + (unreachable) + ) + ) + ) + ) + (return) + ) + ) + ) + (else + (block + (block $label$C + (if + (unreachable) + (then + (br_table $label$C $label$C ;; this all looks mergeable, and is, B ~~ C + (unreachable) + ) + ) + ) + ) + (return) + ) + ) + ) + ) + ) + ;; CHECK: (func $leave-inner-block-type (type $0) + ;; CHECK-NEXT: (block $label$1 + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $label$2 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label$1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $leave-inner-block-type + (block $label$1 + (drop + (block $label$2 (result i32) ;; leave this alone (otherwise, if we make it unreachable, we need to do more updating) + (br_if $label$2 + (unreachable) + (unreachable) + ) + (drop + (i32.const 1) + ) + (br $label$1) + ) + ) + (drop + (i32.const 1) + ) + ) + ) +) +(module + ;; CHECK: (type $0 (func (result i32))) + + ;; CHECK: (memory $0 1 1 shared) + (memory $0 1 1 shared) + ;; CHECK: (export "func_2224" (func $0)) + (export "func_2224" (func $0)) + ;; CHECK: (func $0 (type $0) (result i32) + ;; CHECK-NEXT: (local $var$0 i32) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.load offset=22 + ;; CHECK-NEXT: (local.get $var$0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (i32.atomic.load offset=22 + ;; CHECK-NEXT: (local.get $var$0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $0 (result i32) + (local $var$0 i32) + (if (result i32) + (i32.const 0) + (then + (i32.load offset=22 + (local.get $var$0) + ) + ) + (else + (i32.atomic.load offset=22 + (local.get $var$0) + ) + ) + ) + ) +) +(module + ;; CHECK: (type $0 (func)) + (type $0 (func)) + ;; CHECK: (type $1 (func (param i32))) + + ;; CHECK: (global $global$0 (mut i32) (i32.const 10)) + (global $global$0 (mut i32) (i32.const 10)) + ;; CHECK: (func $determinism (type $0) + ;; CHECK-NEXT: (block $folding-inner0 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $label$1 + ;; CHECK-NEXT: (br_if $label$1 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $folding-inner0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $label$2 + ;; CHECK-NEXT: (br_if $label$2 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (global.get $global$0) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (br $folding-inner0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (global.get $global$0) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (br $folding-inner0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $global$0 + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $global$0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $determinism (; 0 ;) (type $0) + (block $label$1 + (br_if $label$1 + (i32.const 1) + ) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (unreachable) + ) + (block $label$2 + (br_if $label$2 + (i32.const 0) + ) + (if + (global.get $global$0) + (then + (block + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (unreachable) + ) + ) + ) + (unreachable) + ) + (if + (global.get $global$0) + (then + (block + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (unreachable) + ) + ) + ) + (unreachable) + ) + ;; CHECK: (func $careful-of-the-switch (type $1) (param $0 i32) + ;; CHECK-NEXT: (block $label$1 + ;; CHECK-NEXT: (block $label$3 + ;; CHECK-NEXT: (block $label$5 + ;; CHECK-NEXT: (block $label$7 + ;; CHECK-NEXT: (br_table $label$3 $label$7 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label$1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $label$8 + ;; CHECK-NEXT: (br_table $label$3 $label$8 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label$1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $careful-of-the-switch (param $0 i32) + (block $label$1 + (block $label$3 + (block $label$5 + (block $label$7 ;; this is block is equal to $label$8 when accounting for the internal 7/8 difference + (br_table $label$3 $label$7 ;; the reference to $label$3 must remain valid, cannot hoist out of it! + (i32.const 0) + ) + ) + (br $label$1) + ) + (block $label$8 + (br_table $label$3 $label$8 + (i32.const 0) + ) + ) + (br $label$1) + ) + (unreachable) + ) + ) +) + +(module + ;; CHECK: (type $0 (func)) + + ;; CHECK: (func $br-on-null (type $0) + ;; CHECK-NEXT: (block $block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_on_null $block + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (call $br-on-null) + ;; CHECK-NEXT: (br $block) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $br-on-null) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br-on-null + (block $block + (drop + ;; The other two tails are the same, but this br_on_null should inhibit code + ;; folding. + (br_on_null $block + (ref.null none) + ) + ) + (drop + (block (result i32) + (call $br-on-null) + (br $block) + ) + ) + (call $br-on-null) + ) + ) +) diff --git a/test/lit/passes/code-folding_enable-threads.wast b/test/lit/passes/code-folding_enable-threads.wast deleted file mode 100644 index b07000278..000000000 --- a/test/lit/passes/code-folding_enable-threads.wast +++ /dev/null @@ -1,484 +0,0 @@ -;; 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 --code-folding --enable-threads -S -o - | filecheck %s - -(module - ;; CHECK: (type $0 (func)) - - ;; CHECK: (type $1 (func (result f32))) - - ;; CHECK: (type $13 (func (param f32))) - (type $13 (func (param f32))) - (table 282 282 funcref) - ;; CHECK: (memory $0 1 1) - (memory $0 1 1) - ;; CHECK: (table $0 282 282 funcref) - - ;; CHECK: (func $0 - ;; CHECK-NEXT: (block $label$1 - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (block $label$3 - ;; CHECK-NEXT: (call_indirect (type $13) - ;; CHECK-NEXT: (block $label$4 - ;; CHECK-NEXT: (br $label$3) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 105) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $0 - (block $label$1 - (if - (i32.const 1) - (then - (block $label$3 - (call_indirect (type $13) - (block $label$4 (result f32) ;; but this type may change dangerously - (nop) ;; fold this - (br $label$3) - ) - (i32.const 105) - ) - (nop) ;; with this - ) - ) - ) - ) - ) - ;; CHECK: (func $negative-zero (result f32) - ;; CHECK-NEXT: (if (result f32) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (block $label$0 (result f32) - ;; CHECK-NEXT: (f32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (block $label$1 (result f32) - ;; CHECK-NEXT: (f32.const -0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $negative-zero (result f32) - (if (result f32) - (i32.const 0) - (then - (block $label$0 (result f32) - (f32.const 0) - ) - ) - (else - (block $label$1 (result f32) - (f32.const -0) - ) - ) - ) - ) - ;; CHECK: (func $negative-zero-b (result f32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block $label$0 (result f32) - ;; CHECK-NEXT: (f32.const -0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $negative-zero-b (result f32) - (if (result f32) - (i32.const 0) - (then - (block $label$0 (result f32) - (f32.const -0) - ) - ) - (else - (block $label$1 (result f32) - (f32.const -0) - ) - ) - ) - ) - ;; CHECK: (func $negative-zero-c (result f32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block $label$0 (result f32) - ;; CHECK-NEXT: (f32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $negative-zero-c (result f32) - (if (result f32) - (i32.const 0) - (then - (block $label$0 (result f32) - (f32.const 0) - ) - ) - (else - (block $label$1 (result f32) - (f32.const 0) - ) - ) - ) - ) - ;; CHECK: (func $break-target-outside-of-return-merged-code - ;; CHECK-NEXT: (block $label$A - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block $label$B - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (br_table $label$A $label$B - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (block $label$C - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (br_table $label$A $label$C - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $break-target-outside-of-return-merged-code - (block $label$A - (if - (unreachable) - (then - (block - (block - (block $label$B - (if - (unreachable) - (then - (br_table $label$A $label$B - (unreachable) - ) - ) - ) - ) - (return) - ) - ) - ) - (else - (block - (block $label$C - (if - (unreachable) - (then - (br_table $label$A $label$C ;; this all looks mergeable, but $label$A is outside - (unreachable) - ) - ) - ) - ) - (return) - ) - ) - ) - ) - ) - ;; CHECK: (func $break-target-inside-all-good - ;; CHECK-NEXT: (block $folding-inner0 - ;; CHECK-NEXT: (block $label$A - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (br $folding-inner0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (br $folding-inner0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block $label$B - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (br_table $label$B $label$B - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return) - ;; CHECK-NEXT: ) - (func $break-target-inside-all-good - (block $label$A - (if - (unreachable) - (then - (block - (block - (block $label$B - (if - (unreachable) - (then - (br_table $label$B $label$B - (unreachable) - ) - ) - ) - ) - (return) - ) - ) - ) - (else - (block - (block $label$C - (if - (unreachable) - (then - (br_table $label$C $label$C ;; this all looks mergeable, and is, B ~~ C - (unreachable) - ) - ) - ) - ) - (return) - ) - ) - ) - ) - ) - ;; CHECK: (func $leave-inner-block-type - ;; CHECK-NEXT: (block $label$1 - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $label$2 - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $label$1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $leave-inner-block-type - (block $label$1 - (drop - (block $label$2 (result i32) ;; leave this alone (otherwise, if we make it unreachable, we need to do more updating) - (br_if $label$2 - (unreachable) - (unreachable) - ) - (drop - (i32.const 1) - ) - (br $label$1) - ) - ) - (drop - (i32.const 1) - ) - ) - ) -) -(module - ;; CHECK: (type $0 (func (result i32))) - - ;; CHECK: (memory $0 1 1 shared) - (memory $0 1 1 shared) - ;; CHECK: (export "func_2224" (func $0)) - (export "func_2224" (func $0)) - ;; CHECK: (func $0 (result i32) - ;; CHECK-NEXT: (local $var$0 i32) - ;; CHECK-NEXT: (if (result i32) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (i32.load offset=22 - ;; CHECK-NEXT: (local.get $var$0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (i32.atomic.load offset=22 - ;; CHECK-NEXT: (local.get $var$0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $0 (result i32) - (local $var$0 i32) - (if (result i32) - (i32.const 0) - (then - (i32.load offset=22 - (local.get $var$0) - ) - ) - (else - (i32.atomic.load offset=22 - (local.get $var$0) - ) - ) - ) - ) -) -(module - ;; CHECK: (type $0 (func)) - (type $0 (func)) - ;; CHECK: (type $1 (func (param i32))) - - ;; CHECK: (global $global$0 (mut i32) (i32.const 10)) - (global $global$0 (mut i32) (i32.const 10)) - ;; CHECK: (func $determinism - ;; CHECK-NEXT: (block $folding-inner0 - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block $label$1 - ;; CHECK-NEXT: (br_if $label$1 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $folding-inner0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block $label$2 - ;; CHECK-NEXT: (br_if $label$2 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (global.get $global$0) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (br $folding-inner0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (global.get $global$0) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (br $folding-inner0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $global$0 - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $global$0) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - (func $determinism (; 0 ;) (type $0) - (block $label$1 - (br_if $label$1 - (i32.const 1) - ) - (global.set $global$0 - (i32.sub - (global.get $global$0) - (i32.const 1) - ) - ) - (unreachable) - ) - (block $label$2 - (br_if $label$2 - (i32.const 0) - ) - (if - (global.get $global$0) - (then - (block - (global.set $global$0 - (i32.sub - (global.get $global$0) - (i32.const 1) - ) - ) - (unreachable) - ) - ) - ) - (unreachable) - ) - (if - (global.get $global$0) - (then - (block - (global.set $global$0 - (i32.sub - (global.get $global$0) - (i32.const 1) - ) - ) - (unreachable) - ) - ) - ) - (unreachable) - ) - ;; CHECK: (func $careful-of-the-switch (param $0 i32) - ;; CHECK-NEXT: (block $label$1 - ;; CHECK-NEXT: (block $label$3 - ;; CHECK-NEXT: (block $label$5 - ;; CHECK-NEXT: (block $label$7 - ;; CHECK-NEXT: (br_table $label$3 $label$7 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $label$1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block $label$8 - ;; CHECK-NEXT: (br_table $label$3 $label$8 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $label$1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $careful-of-the-switch (param $0 i32) - (block $label$1 - (block $label$3 - (block $label$5 - (block $label$7 ;; this is block is equal to $label$8 when accounting for the internal 7/8 difference - (br_table $label$3 $label$7 ;; the reference to $label$3 must remain valid, cannot hoist out of it! - (i32.const 0) - ) - ) - (br $label$1) - ) - (block $label$8 - (br_table $label$3 $label$8 - (i32.const 0) - ) - ) - (br $label$1) - ) - (unreachable) - ) - ) -) -- cgit v1.2.3