Skip to content

Commit 44a5a4b

Browse files
committed
[WebAssembly] Fix bugs in BLOCK/TRY placement
Summary: Before we placed all TRY/END_TRY markers before placing BLOCK/END_BLOCK markers. This couldn't handle this case: ``` bb0: br bb2 bb1: // nearest common dominator of bb3 and bb4 br_if ... bb3 br bb4 bb2: ... bb3: call @foo // unwinds to ehpad bb4: call @bar // unwinds to ehpad ehpad: catch ... ``` When we placed TRY markers, we placed it in bb1 because it is the nearest common dominator of bb3 and bb4. But because bb0 jumps to bb2, when we placed block markers, we ended up with interleaved scopes like ``` block try end_block catch end_try ``` which was not correct. This patch fixes the bug by placing BLOCK and TRY markers in one pass while iterating BBs in a function. This also adds some more routines to `placeTryMarkers`, because we now have to assume that there can be previously placed BLOCK and END_BLOCK. Reviewers: dschuff Subscribers: sunfish, sbc100, jgravelle-google, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59739 llvm-svn: 357007
1 parent 5c4fad0 commit 44a5a4b

File tree

2 files changed

+95
-42
lines changed

2 files changed

+95
-42
lines changed

llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,7 @@ void WebAssemblyCFGStackify::unregisterScope(MachineInstr *Begin) {
193193

194194
/// Insert a BLOCK marker for branches to MBB (if needed).
195195
void WebAssemblyCFGStackify::placeBlockMarker(MachineBasicBlock &MBB) {
196-
// This should have been handled in placeTryMarker.
197-
if (MBB.isEHPad())
198-
return;
199-
196+
assert(!MBB.isEHPad());
200197
MachineFunction &MF = *MBB.getParent();
201198
auto &MDT = getAnalysis<MachineDominatorTree>();
202199
const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
@@ -253,24 +250,23 @@ void WebAssemblyCFGStackify::placeBlockMarker(MachineBasicBlock &MBB) {
253250
// Instructions that should go after the BLOCK.
254251
SmallPtrSet<const MachineInstr *, 4> AfterSet;
255252
for (const auto &MI : *Header) {
256-
// If there is a previously placed LOOP/TRY marker and the bottom block of
257-
// the loop/exception is above MBB, it should be after the BLOCK, because
258-
// the loop/exception is nested in this block. Otherwise it should be before
259-
// the BLOCK.
260-
if (MI.getOpcode() == WebAssembly::LOOP ||
261-
MI.getOpcode() == WebAssembly::TRY) {
262-
auto *BottomBB = BeginToEnd[&MI]->getParent()->getPrevNode();
263-
if (MBB.getNumber() > BottomBB->getNumber())
253+
// If there is a previously placed LOOP marker and the bottom block of the
254+
// loop is above MBB, it should be after the BLOCK, because the loop is
255+
// nested in this BLOCK. Otherwise it should be before the BLOCK.
256+
if (MI.getOpcode() == WebAssembly::LOOP) {
257+
auto *LoopBottom = BeginToEnd[&MI]->getParent()->getPrevNode();
258+
if (MBB.getNumber() > LoopBottom->getNumber())
264259
AfterSet.insert(&MI);
265260
#ifndef NDEBUG
266261
else
267262
BeforeSet.insert(&MI);
268263
#endif
269264
}
270265

271-
// All previously inserted BLOCK markers should be after the BLOCK because
272-
// they are all nested blocks.
273-
if (MI.getOpcode() == WebAssembly::BLOCK)
266+
// All previously inserted BLOCK/TRY markers should be after the BLOCK
267+
// because they are all nested blocks.
268+
if (MI.getOpcode() == WebAssembly::BLOCK ||
269+
MI.getOpcode() == WebAssembly::TRY)
274270
AfterSet.insert(&MI);
275271

276272
#ifndef NDEBUG
@@ -428,9 +424,7 @@ void WebAssemblyCFGStackify::placeLoopMarker(MachineBasicBlock &MBB) {
428424
}
429425

430426
void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) {
431-
if (!MBB.isEHPad())
432-
return;
433-
427+
assert(MBB.isEHPad());
434428
MachineFunction &MF = *MBB.getParent();
435429
auto &MDT = getAnalysis<MachineDominatorTree>();
436430
const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
@@ -486,31 +480,34 @@ void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) {
486480

487481
// Decide where in Header to put the TRY.
488482

489-
// Instructions that should go before the BLOCK.
483+
// Instructions that should go before the TRY.
490484
SmallPtrSet<const MachineInstr *, 4> BeforeSet;
491-
// Instructions that should go after the BLOCK.
485+
// Instructions that should go after the TRY.
492486
SmallPtrSet<const MachineInstr *, 4> AfterSet;
493487
for (const auto &MI : *Header) {
494-
// If there is a previously placed LOOP marker and the bottom block of
495-
// the loop is above MBB, the LOOP should be after the TRY, because the
496-
// loop is nested in this try. Otherwise it should be before the TRY.
488+
// If there is a previously placed LOOP marker and the bottom block of the
489+
// loop is above MBB, it should be after the TRY, because the loop is nested
490+
// in this TRY. Otherwise it should be before the TRY.
497491
if (MI.getOpcode() == WebAssembly::LOOP) {
498-
if (MBB.getNumber() > Bottom->getNumber())
492+
auto *LoopBottom = BeginToEnd[&MI]->getParent()->getPrevNode();
493+
if (MBB.getNumber() > LoopBottom->getNumber())
499494
AfterSet.insert(&MI);
500495
#ifndef NDEBUG
501496
else
502497
BeforeSet.insert(&MI);
503498
#endif
504499
}
505500

506-
// All previously inserted TRY markers should be after the TRY because they
507-
// are all nested trys.
508-
if (MI.getOpcode() == WebAssembly::TRY)
501+
// All previously inserted BLOCK/TRY markers should be after the TRY because
502+
// they are all nested trys.
503+
if (MI.getOpcode() == WebAssembly::BLOCK ||
504+
MI.getOpcode() == WebAssembly::TRY)
509505
AfterSet.insert(&MI);
510506

511507
#ifndef NDEBUG
512-
// All END_(LOOP/TRY) markers should be before the TRY.
513-
if (MI.getOpcode() == WebAssembly::END_LOOP ||
508+
// All END_(BLOCK/LOOP/TRY) markers should be before the TRY.
509+
if (MI.getOpcode() == WebAssembly::END_BLOCK ||
510+
MI.getOpcode() == WebAssembly::END_LOOP ||
514511
MI.getOpcode() == WebAssembly::END_TRY)
515512
BeforeSet.insert(&MI);
516513
#endif
@@ -566,8 +563,9 @@ void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) {
566563
AfterSet.clear();
567564
for (const auto &MI : *Cont) {
568565
#ifndef NDEBUG
569-
// END_TRY should precede existing LOOP markers.
570-
if (MI.getOpcode() == WebAssembly::LOOP)
566+
// END_TRY should precede existing LOOP and BLOCK markers.
567+
if (MI.getOpcode() == WebAssembly::LOOP ||
568+
MI.getOpcode() == WebAssembly::BLOCK)
571569
AfterSet.insert(&MI);
572570

573571
// All END_TRY markers placed earlier belong to exceptions that contains
@@ -588,6 +586,8 @@ void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) {
588586
AfterSet.insert(&MI);
589587
#endif
590588
}
589+
590+
// It is not possible for an END_BLOCK to be already in this block.
591591
}
592592

593593
// Mark the end of the TRY.
@@ -774,15 +774,19 @@ void WebAssemblyCFGStackify::placeMarkers(MachineFunction &MF) {
774774
// Place the LOOP for MBB if MBB is the header of a loop.
775775
for (auto &MBB : MF)
776776
placeLoopMarker(MBB);
777-
// Place the TRY for MBB if MBB is the EH pad of an exception.
777+
778778
const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo();
779-
if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm &&
780-
MF.getFunction().hasPersonalityFn())
781-
for (auto &MBB : MF)
782-
placeTryMarker(MBB);
783-
// Place the BLOCK for MBB if MBB is branched to from above.
784-
for (auto &MBB : MF)
785-
placeBlockMarker(MBB);
779+
for (auto &MBB : MF) {
780+
if (MBB.isEHPad()) {
781+
// Place the TRY for MBB if MBB is the EH pad of an exception.
782+
if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm &&
783+
MF.getFunction().hasPersonalityFn())
784+
placeTryMarker(MBB);
785+
} else {
786+
// Place the BLOCK for MBB if MBB is branched to from above.
787+
placeBlockMarker(MBB);
788+
}
789+
}
786790
}
787791

788792
void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) {

llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling | FileCheck %s
2+
; RUN: llc < %s -O0 -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs -exception-model=wasm -mattr=+exception-handling | FileCheck %s --check-prefix=NOOPT
23

34
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
45
target triple = "wasm32-unknown-unknown"
@@ -70,7 +71,7 @@ rethrow: ; preds = %catch.fallthrough
7071
call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
7172
unreachable
7273

73-
try.cont: ; preds = %entry, %catch, %catch2
74+
try.cont: ; preds = %catch, %catch2, %entry
7475
ret void
7576
}
7677

@@ -173,15 +174,15 @@ rethrow5: ; preds = %catch.start3
173174
invoke void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %9) ]
174175
to label %unreachable unwind label %ehcleanup9
175176

176-
try.cont: ; preds = %catch, %invoke.cont8
177+
try.cont: ; preds = %invoke.cont8, %catch
177178
call void @__cxa_end_catch() [ "funclet"(token %1) ]
178179
catchret from %1 to label %try.cont11
179180

180181
rethrow: ; preds = %catch.start
181182
call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
182183
unreachable
183184

184-
try.cont11: ; preds = %entry, %try.cont
185+
try.cont11: ; preds = %try.cont, %entry
185186
ret void
186187

187188
ehcleanup: ; preds = %catch6
@@ -286,6 +287,54 @@ terminate: ; preds = %ehcleanup
286287
unreachable
287288
}
288289

290+
; Tests if block and try markers are correctly placed. Even if two predecessors
291+
; of the EH pad are bb2 and bb3 and their nearest common dominator is bb1, the
292+
; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2,
293+
; and scopes cannot be interleaved.
294+
295+
; NOOPT-LABEL: test3
296+
; NOOPT: try
297+
; NOOPT: block
298+
; NOOPT: block
299+
; NOOPT: block
300+
; NOOPT: end_block
301+
; NOOPT: end_block
302+
; NOOPT: call foo
303+
; NOOPT: end_block
304+
; NOOPT: call bar
305+
; NOOPT: catch {{.*}}
306+
; NOOPT: end_try
307+
define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
308+
bb0:
309+
br i1 undef, label %bb1, label %bb2
310+
311+
bb1: ; preds = %bb0
312+
br i1 undef, label %bb3, label %bb4
313+
314+
bb2: ; preds = %bb0
315+
br label %try.cont
316+
317+
bb3: ; preds = %bb1
318+
invoke void @foo()
319+
to label %try.cont unwind label %catch.dispatch
320+
321+
bb4: ; preds = %bb1
322+
invoke void @bar()
323+
to label %try.cont unwind label %catch.dispatch
324+
325+
catch.dispatch: ; preds = %bb4, %bb3
326+
%0 = catchswitch within none [label %catch.start] unwind to caller
327+
328+
catch.start: ; preds = %catch.dispatch
329+
%1 = catchpad within %0 [i8* null]
330+
%2 = call i8* @llvm.wasm.get.exception(token %1)
331+
%3 = call i32 @llvm.wasm.get.ehselector(token %1)
332+
catchret from %1 to label %try.cont
333+
334+
try.cont: ; preds = %catch.start, %bb4, %bb3, %bb2
335+
ret void
336+
}
337+
289338
declare void @foo()
290339
declare void @bar()
291340
declare i32 @__gxx_wasm_personality_v0(...)

0 commit comments

Comments
 (0)