Skip to content

Commit 1230f21

Browse files
aheejinmemfrob
authored and
memfrob
committed
[WebAssemblly] Fix rethrow's argument computation
Previously we assumed `rethrow`'s argument was always 0, but it turned out `rethrow` follows the same rule with `br` or `delegate`: WebAssembly/exception-handling#137 WebAssembly/exception-handling#146 (comment) Currently `rethrow`s generated by our backend always rethrow the exception caught by the innermost enclosing catch, so this adds a function to compute that and replaces `rethrow`'s argument with its computed result. This also renames `EHPadStack` in `InstPrinter` to `TryStack`, because in CFGStackify we use `EHPadStack` to mean the range between `catch`~`end`, while in `InstPrinter` we used it to mean the range between `try`~`catch`, so choosing different names would look clearer. Doesn't contain any functional changes in `InstPrinter`. Reviewed By: dschuff Differential Revision: https://reviews.llvm.org/D96595
1 parent ccb24f3 commit 1230f21

File tree

6 files changed

+129
-18
lines changed

6 files changed

+129
-18
lines changed

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp

+8-10
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
104104
case WebAssembly::TRY:
105105
case WebAssembly::TRY_S:
106106
ControlFlowStack.push_back(std::make_pair(ControlFlowCounter, false));
107-
EHPadStack.push_back(ControlFlowCounter++);
107+
TryStack.push_back(ControlFlowCounter++);
108108
EHInstStack.push_back(TRY);
109109
return;
110110

@@ -149,11 +149,10 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
149149
} else if (EHInstStack.back() == CATCH_ALL) {
150150
printAnnotation(OS, "catch/catch_all cannot occur after catch_all");
151151
} else if (EHInstStack.back() == TRY) {
152-
if (EHPadStack.empty()) {
152+
if (TryStack.empty()) {
153153
printAnnotation(OS, "try-catch mismatch!");
154154
} else {
155-
printAnnotation(OS,
156-
"catch" + utostr(EHPadStack.pop_back_val()) + ':');
155+
printAnnotation(OS, "catch" + utostr(TryStack.pop_back_val()) + ':');
157156
}
158157
EHInstStack.pop_back();
159158
if (Opc == WebAssembly::CATCH || Opc == WebAssembly::CATCH_S) {
@@ -168,28 +167,27 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
168167
case WebAssembly::RETHROW_S:
169168
// 'rethrow' rethrows to the nearest enclosing catch scope, if any. If
170169
// there's no enclosing catch scope, it throws up to the caller.
171-
if (EHPadStack.empty()) {
170+
if (TryStack.empty()) {
172171
printAnnotation(OS, "to caller");
173172
} else {
174-
printAnnotation(OS, "down to catch" + utostr(EHPadStack.back()));
173+
printAnnotation(OS, "down to catch" + utostr(TryStack.back()));
175174
}
176175
return;
177176

178177
case WebAssembly::DELEGATE:
179178
case WebAssembly::DELEGATE_S:
180-
if (ControlFlowStack.empty() || EHPadStack.empty() ||
181-
EHInstStack.empty()) {
179+
if (ControlFlowStack.empty() || TryStack.empty() || EHInstStack.empty()) {
182180
printAnnotation(OS, "try-delegate mismatch!");
183181
} else {
184182
// 'delegate' is
185183
// 1. A marker for the end of block label
186184
// 2. A destination for throwing instructions
187185
// 3. An instruction that itself rethrows to another 'catch'
188-
assert(ControlFlowStack.back().first == EHPadStack.back());
186+
assert(ControlFlowStack.back().first == TryStack.back());
189187
std::string Label = "label/catch" +
190188
utostr(ControlFlowStack.pop_back_val().first) +
191189
": ";
192-
EHPadStack.pop_back();
190+
TryStack.pop_back();
193191
EHInstStack.pop_back();
194192
uint64_t Depth = MI->getOperand(0).getImm();
195193
if (Depth >= ControlFlowStack.size()) {

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class MCSubtargetInfo;
2626
class WebAssemblyInstPrinter final : public MCInstPrinter {
2727
uint64_t ControlFlowCounter = 0;
2828
SmallVector<std::pair<uint64_t, bool>, 4> ControlFlowStack;
29-
SmallVector<uint64_t, 4> EHPadStack;
29+
SmallVector<uint64_t, 4> TryStack;
3030

3131
enum EHInstKind { TRY, CATCH, CATCH_ALL };
3232
SmallVector<EHInstKind, 4> EHInstStack;

llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp

+55-1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ class WebAssemblyCFGStackify final : public MachineFunctionPass {
8686
const MachineBasicBlock *MBB);
8787
unsigned getDelegateDepth(const SmallVectorImpl<EndMarkerInfo> &Stack,
8888
const MachineBasicBlock *MBB);
89+
unsigned
90+
getRethrowDepth(const SmallVectorImpl<EndMarkerInfo> &Stack,
91+
const SmallVectorImpl<const MachineBasicBlock *> &EHPadStack);
8992
void rewriteDepthImmediates(MachineFunction &MF);
9093
void fixEndsAtEndOfFunction(MachineFunction &MF);
9194
void cleanupFunctionData(MachineFunction &MF);
@@ -1551,9 +1554,48 @@ unsigned WebAssemblyCFGStackify::getDelegateDepth(
15511554
return Depth;
15521555
}
15531556

1557+
unsigned WebAssemblyCFGStackify::getRethrowDepth(
1558+
const SmallVectorImpl<EndMarkerInfo> &Stack,
1559+
const SmallVectorImpl<const MachineBasicBlock *> &EHPadStack) {
1560+
unsigned Depth = 0;
1561+
// In our current implementation, rethrows always rethrow the exception caught
1562+
// by the innermost enclosing catch. This means while traversing Stack in the
1563+
// reverse direction, when we encounter END_TRY, we should check if the
1564+
// END_TRY corresponds to the current innermost EH pad. For example:
1565+
// try
1566+
// ...
1567+
// catch ;; (a)
1568+
// try
1569+
// rethrow 1 ;; (b)
1570+
// catch ;; (c)
1571+
// rethrow 0 ;; (d)
1572+
// end ;; (e)
1573+
// end ;; (f)
1574+
//
1575+
// When we are at 'rethrow' (d), while reversely traversing Stack the first
1576+
// 'end' we encounter is the 'end' (e), which corresponds to the 'catch' (c).
1577+
// And 'rethrow' (d) rethrows the exception caught by 'catch' (c), so we stop
1578+
// there and the depth should be 0. But when we are at 'rethrow' (b), it
1579+
// rethrows the exception caught by 'catch' (a), so when traversing Stack
1580+
// reversely, we should skip the 'end' (e) and choose 'end' (f), which
1581+
// corresponds to 'catch' (a).
1582+
for (auto X : reverse(Stack)) {
1583+
const MachineInstr *End = X.second;
1584+
if (End->getOpcode() == WebAssembly::END_TRY) {
1585+
auto *EHPad = TryToEHPad[EndToBegin[End]];
1586+
if (EHPadStack.back() == EHPad)
1587+
break;
1588+
}
1589+
++Depth;
1590+
}
1591+
assert(Depth < Stack.size() && "Rethrow destination should be in scope");
1592+
return Depth;
1593+
}
1594+
15541595
void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) {
15551596
// Now rewrite references to basic blocks to be depth immediates.
15561597
SmallVector<EndMarkerInfo, 8> Stack;
1598+
SmallVector<const MachineBasicBlock *, 8> EHPadStack;
15571599
for (auto &MBB : reverse(MF)) {
15581600
for (auto I = MBB.rbegin(), E = MBB.rend(); I != E; ++I) {
15591601
MachineInstr &MI = *I;
@@ -1575,16 +1617,28 @@ void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) {
15751617
Stack.push_back(std::make_pair(&MBB, &MI));
15761618
break;
15771619

1578-
case WebAssembly::END_TRY:
1620+
case WebAssembly::END_TRY: {
15791621
// We handle DELEGATE in the default level, because DELEGATE has
15801622
// immediate operands to rewrite.
15811623
Stack.push_back(std::make_pair(&MBB, &MI));
1624+
auto *EHPad = TryToEHPad[EndToBegin[&MI]];
1625+
EHPadStack.push_back(EHPad);
15821626
break;
1627+
}
15831628

15841629
case WebAssembly::END_LOOP:
15851630
Stack.push_back(std::make_pair(EndToBegin[&MI]->getParent(), &MI));
15861631
break;
15871632

1633+
case WebAssembly::CATCH:
1634+
case WebAssembly::CATCH_ALL:
1635+
EHPadStack.pop_back();
1636+
break;
1637+
1638+
case WebAssembly::RETHROW:
1639+
MI.getOperand(0).setImm(getRethrowDepth(Stack, EHPadStack));
1640+
break;
1641+
15881642
default:
15891643
if (MI.isTerminator()) {
15901644
// Rewrite MBB operands to be depth immediates.

llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td

+2-2
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ defm THROW : I<(outs), (ins event_op:$tag, variable_ops),
133133
"throw \t$tag", "throw \t$tag", 0x08>;
134134
defm RETHROW : NRI<(outs), (ins i32imm:$depth), [], "rethrow \t$depth", 0x09>;
135135
} // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
136-
// For C++ support, we only rethrow the latest exception, thus always setting
137-
// the depth to 0.
136+
// The depth argument will be computed in CFGStackify. We set it to 0 here for
137+
// now.
138138
def : Pat<(int_wasm_rethrow), (RETHROW 0)>;
139139

140140
// Region within which an exception is caught: try / end_try

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ try.cont: ; preds = %catch, %catch2, %en
119119
; CHECK: rethrow 0 # down to catch[[C0:[0-9]+]]
120120
; CHECK: end_try
121121
; CHECK: end_block # label[[L2]]:
122-
; CHECK: rethrow 0 # down to catch[[C0]]
122+
; CHECK: rethrow 1 # down to catch[[C0]]
123123
; CHECK: catch_all # catch[[C0]]:
124124
; CHECK: call __cxa_end_catch
125125
; CHECK: rethrow 0 # to caller
@@ -128,7 +128,7 @@ try.cont: ; preds = %catch, %catch2, %en
128128
; CHECK: br 2 # 2: down to label[[L1]]
129129
; CHECK: end_try
130130
; CHECK: end_block # label[[L0]]:
131-
; CHECK: rethrow 0 # to caller
131+
; CHECK: rethrow 1 # to caller
132132
; CHECK: end_block # label[[L1]]:
133133
; CHECK: call __cxa_end_catch
134134
; CHECK: end_try

llvm/test/CodeGen/WebAssembly/exception.mir

+61-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
define void @unreachable_ehpad_test() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
1313
ret void
1414
}
15+
define void @rethrow_arg_test() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
16+
ret void
17+
}
1518
...
1619

1720
---
@@ -24,6 +27,7 @@ liveins:
2427
body: |
2528
bb.0:
2629
; TRY should be before EH_LABEL wrappers of throwing calls
30+
; CHECK: bb.0
2731
; CHECK: TRY
2832
; CHECK-NEXT: EH_LABEL
2933
; CHECK-NEXT: CALL @foo
@@ -38,15 +42,17 @@ body: |
3842
; predecessors: %bb.0
3943
successors: %bb.2
4044
; CATCH_ALL should be after an EH_LABEL at the beginning of an EH pad
45+
; CHECK: bb.1
4146
; CHECK: EH_LABEL
4247
; CHECK-NEXT: CATCH_ALL
4348
EH_LABEL <mcsymbol .Ltmp2>
44-
CATCHRET %bb.2, %bb.0, implicit-def dead $arguments
49+
CATCHRET %bb.2, %bb.1, implicit-def dead $arguments
4550
4651
bb.2:
4752
; predecessors: %bb.0, %bb.1
4853
RETURN implicit-def dead $arguments
4954
...
55+
5056
---
5157
# Unreachable EH pads should be removed by LateEHPrepare.
5258
# CHECK-LABEL: name: unreachable_ehpad_test
@@ -64,10 +70,63 @@ body: |
6470
bb.1 (landing-pad):
6571
successors: %bb.2
6672
EH_LABEL <mcsymbol .Ltmp2>
67-
CATCHRET %bb.2, %bb.0, implicit-def dead $arguments
73+
CATCHRET %bb.2, %bb.1, implicit-def dead $arguments
6874
6975
; CHECK: bb.2
7076
bb.2:
7177
; predecessors: %bb.0, %bb.1
7278
RETURN implicit-def dead $arguments
7379
...
80+
81+
---
82+
# CHECK-LABEL: name: rethrow_arg_test
83+
name: rethrow_arg_test
84+
liveins:
85+
- { reg: '$arguments' }
86+
body: |
87+
bb.0:
88+
successors: %bb.1, %bb.4
89+
; CHECK: bb.0
90+
; CHECK: TRY
91+
EH_LABEL <mcsymbol .Ltmp0>
92+
CALL @foo, implicit-def dead $arguments, implicit $sp32, implicit $sp64
93+
EH_LABEL <mcsymbol .Ltmp1>
94+
BR %bb.4, implicit-def dead $arguments
95+
96+
bb.1 (landing-pad):
97+
; predecessors: %bb.0
98+
successors: %bb.2
99+
; CHECK: bb.1
100+
; CHECK: CATCH
101+
; CHECK: TRY
102+
; This RETHROW rethrows the exception caught by this BB's CATCH, but after
103+
; CFGStackify a TRY is placed between the CATCH and this RETHROW, so after
104+
; CFGStackify its immediate argument should become not 0, but 1.
105+
; CHECK: RETHROW 1
106+
EH_LABEL <mcsymbol .Ltmp2>
107+
%0:i32 = CATCH &__cpp_exception, implicit-def dead $arguments
108+
RETHROW 0, implicit-def dead $arguments
109+
110+
bb.2 (landing-pad):
111+
; predecessors: %bb.1
112+
successors: %bb.3
113+
; CHECK: bb.2
114+
; CHECK: CATCH
115+
; CHECK: RETHROW 0
116+
EH_LABEL <mcsymbol .Ltmp5>
117+
%1:i32 = CATCH &__cpp_exception, implicit-def dead $arguments
118+
RETHROW 0, implicit-def dead $arguments
119+
CATCHRET %bb.3, %bb.2, implicit-def dead $arguments
120+
121+
bb.3:
122+
; predecessors: %bb.2
123+
successors: %bb.4
124+
CATCHRET %bb.4, %bb.1, implicit-def dead $arguments
125+
126+
bb.4:
127+
; predecessors: %bb.0, %bb.3
128+
; CHECK: bb.4
129+
; CHECK: END_TRY
130+
; CHECK: END_TRY
131+
RETURN implicit-def dead $arguments
132+

0 commit comments

Comments
 (0)