Skip to content

Commit 17b024a

Browse files
authored
RemoveUnusedBrs: Refinalize when removing a break (#7918)
We refinalized when changing a break's type, but not when removing it entirely. But that removal can affect other types, so we need to. Looks like a regression from #7639, which was very hard for the fuzzer to find. The issue is that we end up with types not fully refinalized, but on control flow, that the validator doesn't fully check. The concrete issue was some weird sequence of passes that leads to an assertion inside SimplifyLocals; I extracted a simpler testcase using debug annotations.
1 parent 3f6729b commit 17b024a

2 files changed

Lines changed: 75 additions & 2 deletions

File tree

src/passes/RemoveUnusedBrs.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,8 +2024,6 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
20242024
block->list.push_back(curr);
20252025
block->finalize();
20262026
BranchHints::clear(curr, getFunction());
2027-
// The type changed, so refinalize.
2028-
refinalize = true;
20292027
} else {
20302028
// the branch is never taken, allow control flow to fall through
20312029
if (curr->value) {
@@ -2034,6 +2032,11 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
20342032
}
20352033
}
20362034
replaceCurrent(block);
2035+
// The type changes if we make the break unconditional (it becomes
2036+
// unreachable), but a change may also happen if we see the break is
2037+
// not taken, as then the target block may have no more breaks, and
2038+
// become unreachable. Either way, refinalize.
2039+
refinalize = true;
20372040
}
20382041
}
20392042
};
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
2+
3+
;; RUN: env BINARYEN_PRINT_FULL=1 wasm-opt %s --remove-unused-brs -S -o - | filecheck %s
4+
5+
(module
6+
;; CHECK: (func $test
7+
;; CHECK-NEXT: ;;@
8+
;; CHECK-NEXT: (loop $loop
9+
;; CHECK-NEXT: ;;@
10+
;; CHECK-NEXT: (if
11+
;; CHECK-NEXT: ;;@
12+
;; CHECK-NEXT: (i32.const 0) (; i32 ;)
13+
;; CHECK-NEXT: (then
14+
;; CHECK-NEXT: ;;@
15+
;; CHECK-NEXT: (block $block (; unreachable ;)
16+
;; CHECK-NEXT: ;;@
17+
;; CHECK-NEXT: (br_if $loop
18+
;; CHECK-NEXT: ;;@
19+
;; CHECK-NEXT: (block (result i32) (; i32 ;)
20+
;; CHECK-NEXT: ;;@
21+
;; CHECK-NEXT: (block (; none ;)
22+
;; CHECK-NEXT: ) ;; end block
23+
;; CHECK-NEXT: ;;@
24+
;; CHECK-NEXT: (i32.const 0) (; i32 ;)
25+
;; CHECK-NEXT: ) ;; end block (; i32 ;)
26+
;; CHECK-NEXT: ) (; none ;)
27+
;; CHECK-NEXT: ;;@
28+
;; CHECK-NEXT: (unreachable) (; unreachable ;)
29+
;; CHECK-NEXT: ) ;; end block block (; unreachable ;)
30+
;; CHECK-NEXT: )
31+
;; CHECK-NEXT: (else
32+
;; CHECK-NEXT: ;;@
33+
;; CHECK-NEXT: (unreachable) (; unreachable ;)
34+
;; CHECK-NEXT: )
35+
;; CHECK-NEXT: ) ;; end if (; unreachable ;)
36+
;; CHECK-NEXT: ) ;; end loop loop (; unreachable ;)
37+
;; CHECK-NEXT: )
38+
(func $test
39+
(loop $loop
40+
(if
41+
(i32.const 0)
42+
(then
43+
(block $block
44+
(if
45+
(block (result i32)
46+
;; This can be removed, after which $block has no breaks and it will
47+
;; become unreachable, as verified by PRINT_FULL. (The rest of the test
48+
;; around it is needed to avoid remove-unused-brs optimizing it all
49+
;; away as trivial.)
50+
(br_if $block
51+
(i32.const 0)
52+
)
53+
(i32.const 0)
54+
)
55+
(then
56+
(br $loop)
57+
)
58+
(else
59+
(unreachable)
60+
)
61+
)
62+
)
63+
)
64+
(else
65+
(unreachable)
66+
)
67+
)
68+
)
69+
)
70+
)

0 commit comments

Comments
 (0)