Skip to content

Commit 2968611

Browse files
committed
[WebAssembly] Fix delegate's argument computation
I previously assumed `delegate`'s immediate argument computation followed a different rule than that of branches, but we agreed to make it the same (WebAssembly/exception-handling#146). This removes the need for a separate `DelegateStack` in both CFGStackify and InstPrinter. When computing the immediate argument, we use a different function for `delegate` computation because in MIR `DELEGATE`'s instruction's destination is the destination catch BB or delegate BB, and when it is a catch BB, we need an additional step of getting its corresponding `end` marker. Reviewed By: tlively, dschuff Differential Revision: https://reviews.llvm.org/D96525
1 parent e434fc0 commit 2968611

File tree

4 files changed

+80
-64
lines changed

4 files changed

+80
-64
lines changed

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

+14-18
Original file line numberDiff line numberDiff line change
@@ -93,42 +93,37 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
9393
case WebAssembly::LOOP:
9494
case WebAssembly::LOOP_S:
9595
printAnnotation(OS, "label" + utostr(ControlFlowCounter) + ':');
96-
ControlFlowStack.push_back(std::make_pair(ControlFlowCounter, true));
97-
DelegateStack.push_back(ControlFlowCounter++);
96+
ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, true));
9897
return;
9998

10099
case WebAssembly::BLOCK:
101100
case WebAssembly::BLOCK_S:
102-
ControlFlowStack.push_back(std::make_pair(ControlFlowCounter, false));
103-
DelegateStack.push_back(ControlFlowCounter++);
101+
ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
104102
return;
105103

106104
case WebAssembly::TRY:
107105
case WebAssembly::TRY_S:
108106
ControlFlowStack.push_back(std::make_pair(ControlFlowCounter, false));
109-
EHPadStack.push_back(ControlFlowCounter);
110-
DelegateStack.push_back(ControlFlowCounter++);
107+
EHPadStack.push_back(ControlFlowCounter++);
111108
EHInstStack.push_back(TRY);
112109
return;
113110

114111
case WebAssembly::END_LOOP:
115112
case WebAssembly::END_LOOP_S:
116-
if (ControlFlowStack.empty() || DelegateStack.empty()) {
113+
if (ControlFlowStack.empty()) {
117114
printAnnotation(OS, "End marker mismatch!");
118115
} else {
119116
ControlFlowStack.pop_back();
120-
DelegateStack.pop_back();
121117
}
122118
return;
123119

124120
case WebAssembly::END_BLOCK:
125121
case WebAssembly::END_BLOCK_S:
126-
if (ControlFlowStack.empty() || DelegateStack.empty()) {
122+
if (ControlFlowStack.empty()) {
127123
printAnnotation(OS, "End marker mismatch!");
128124
} else {
129125
printAnnotation(
130126
OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
131-
DelegateStack.pop_back();
132127
}
133128
return;
134129

@@ -154,12 +149,11 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
154149
} else if (EHInstStack.back() == CATCH_ALL) {
155150
printAnnotation(OS, "catch/catch_all cannot occur after catch_all");
156151
} else if (EHInstStack.back() == TRY) {
157-
if (EHPadStack.empty() || DelegateStack.empty()) {
152+
if (EHPadStack.empty()) {
158153
printAnnotation(OS, "try-catch mismatch!");
159154
} else {
160155
printAnnotation(OS,
161156
"catch" + utostr(EHPadStack.pop_back_val()) + ':');
162-
DelegateStack.pop_back();
163157
}
164158
EHInstStack.pop_back();
165159
if (Opc == WebAssembly::CATCH || Opc == WebAssembly::CATCH_S) {
@@ -184,26 +178,28 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
184178
case WebAssembly::DELEGATE:
185179
case WebAssembly::DELEGATE_S:
186180
if (ControlFlowStack.empty() || EHPadStack.empty() ||
187-
DelegateStack.empty() || EHInstStack.empty()) {
181+
EHInstStack.empty()) {
188182
printAnnotation(OS, "try-delegate mismatch!");
189183
} else {
190184
// 'delegate' is
191185
// 1. A marker for the end of block label
192186
// 2. A destination for throwing instructions
193187
// 3. An instruction that itself rethrows to another 'catch'
194-
assert(ControlFlowStack.back().first == EHPadStack.back() &&
195-
EHPadStack.back() == DelegateStack.back());
188+
assert(ControlFlowStack.back().first == EHPadStack.back());
196189
std::string Label = "label/catch" +
197190
utostr(ControlFlowStack.pop_back_val().first) +
198191
": ";
199192
EHPadStack.pop_back();
200-
DelegateStack.pop_back();
201193
EHInstStack.pop_back();
202194
uint64_t Depth = MI->getOperand(0).getImm();
203-
if (Depth >= DelegateStack.size()) {
195+
if (Depth >= ControlFlowStack.size()) {
204196
Label += "to caller";
205197
} else {
206-
Label += "down to catch" + utostr(DelegateStack.rbegin()[Depth]);
198+
const auto &Pair = ControlFlowStack.rbegin()[Depth];
199+
if (Pair.second)
200+
printAnnotation(OS, "delegate cannot target a loop");
201+
else
202+
Label += "down to catch" + utostr(Pair.first);
207203
}
208204
printAnnotation(OS, Label);
209205
}

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

-5
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ class WebAssemblyInstPrinter final : public MCInstPrinter {
2727
uint64_t ControlFlowCounter = 0;
2828
SmallVector<std::pair<uint64_t, bool>, 4> ControlFlowStack;
2929
SmallVector<uint64_t, 4> EHPadStack;
30-
// 'delegate' can target any block-like structure, but in case the target is
31-
// 'try', it rethrows to the corresponding 'catch'. Because it can target all
32-
// blocks but with a slightly different semantics with branches, we need a
33-
// separate stack for 'delegate'.
34-
SmallVector<uint64_t, 4> DelegateStack;
3530

3631
enum EHInstKind { TRY, CATCH, CATCH_ALL };
3732
SmallVector<EHInstKind, 4> EHInstStack;

llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp

+65-40
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,12 @@ class WebAssemblyCFGStackify final : public MachineFunctionPass {
8080
void removeUnnecessaryInstrs(MachineFunction &MF);
8181

8282
// Wrap-up
83-
unsigned getDepth(const SmallVectorImpl<const MachineBasicBlock *> &Stack,
84-
const MachineBasicBlock *MBB);
83+
using EndMarkerInfo =
84+
std::pair<const MachineBasicBlock *, const MachineInstr *>;
85+
unsigned getBranchDepth(const SmallVectorImpl<EndMarkerInfo> &Stack,
86+
const MachineBasicBlock *MBB);
87+
unsigned getDelegateDepth(const SmallVectorImpl<EndMarkerInfo> &Stack,
88+
const MachineBasicBlock *MBB);
8589
void rewriteDepthImmediates(MachineFunction &MF);
8690
void fixEndsAtEndOfFunction(MachineFunction &MF);
8791
void cleanupFunctionData(MachineFunction &MF);
@@ -1399,21 +1403,6 @@ void WebAssemblyCFGStackify::recalculateScopeTops(MachineFunction &MF) {
13991403
}
14001404
}
14011405

1402-
unsigned WebAssemblyCFGStackify::getDepth(
1403-
const SmallVectorImpl<const MachineBasicBlock *> &Stack,
1404-
const MachineBasicBlock *MBB) {
1405-
if (MBB == FakeCallerBB)
1406-
return Stack.size();
1407-
unsigned Depth = 0;
1408-
for (auto X : reverse(Stack)) {
1409-
if (X == MBB)
1410-
break;
1411-
++Depth;
1412-
}
1413-
assert(Depth < Stack.size() && "Branch destination should be in scope");
1414-
return Depth;
1415-
}
1416-
14171406
/// In normal assembly languages, when the end of a function is unreachable,
14181407
/// because the function ends in an infinite loop or a noreturn call or similar,
14191408
/// it isn't necessary to worry about the function return type at the end of
@@ -1515,48 +1504,85 @@ void WebAssemblyCFGStackify::placeMarkers(MachineFunction &MF) {
15151504
}
15161505
}
15171506

1507+
unsigned WebAssemblyCFGStackify::getBranchDepth(
1508+
const SmallVectorImpl<EndMarkerInfo> &Stack, const MachineBasicBlock *MBB) {
1509+
unsigned Depth = 0;
1510+
for (auto X : reverse(Stack)) {
1511+
if (X.first == MBB)
1512+
break;
1513+
++Depth;
1514+
}
1515+
assert(Depth < Stack.size() && "Branch destination should be in scope");
1516+
return Depth;
1517+
}
1518+
1519+
unsigned WebAssemblyCFGStackify::getDelegateDepth(
1520+
const SmallVectorImpl<EndMarkerInfo> &Stack, const MachineBasicBlock *MBB) {
1521+
if (MBB == FakeCallerBB)
1522+
return Stack.size();
1523+
// Delegate's destination is either a catch or a another delegate BB. When the
1524+
// destination is another delegate, we can compute the argument in the same
1525+
// way as branches, because the target delegate BB only contains the single
1526+
// delegate instruction.
1527+
if (!MBB->isEHPad()) // Target is a delegate BB
1528+
return getBranchDepth(Stack, MBB);
1529+
1530+
// When the delegate's destination is a catch BB, we need to use its
1531+
// corresponding try's end_try BB because Stack contains each marker's end BB.
1532+
// Also we need to check if the end marker instruction matches, because a
1533+
// single BB can contain multiple end markers, like this:
1534+
// bb:
1535+
// END_BLOCK
1536+
// END_TRY
1537+
// END_BLOCK
1538+
// END_TRY
1539+
// ...
1540+
//
1541+
// In case of branches getting the immediate that targets any of these is
1542+
// fine, but delegate has to exactly target the correct try.
1543+
unsigned Depth = 0;
1544+
const MachineInstr *EndTry = BeginToEnd[EHPadToTry[MBB]];
1545+
for (auto X : reverse(Stack)) {
1546+
if (X.first == EndTry->getParent() && X.second == EndTry)
1547+
break;
1548+
++Depth;
1549+
}
1550+
assert(Depth < Stack.size() && "Delegate destination should be in scope");
1551+
return Depth;
1552+
}
1553+
15181554
void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) {
15191555
// Now rewrite references to basic blocks to be depth immediates.
1520-
SmallVector<const MachineBasicBlock *, 8> Stack;
1521-
SmallVector<const MachineBasicBlock *, 8> DelegateStack;
1556+
SmallVector<EndMarkerInfo, 8> Stack;
15221557
for (auto &MBB : reverse(MF)) {
15231558
for (auto I = MBB.rbegin(), E = MBB.rend(); I != E; ++I) {
15241559
MachineInstr &MI = *I;
15251560
switch (MI.getOpcode()) {
15261561
case WebAssembly::BLOCK:
15271562
case WebAssembly::TRY:
1528-
assert(ScopeTops[Stack.back()->getNumber()]->getNumber() <=
1563+
assert(ScopeTops[Stack.back().first->getNumber()]->getNumber() <=
15291564
MBB.getNumber() &&
15301565
"Block/try marker should be balanced");
15311566
Stack.pop_back();
1532-
DelegateStack.pop_back();
15331567
break;
15341568

15351569
case WebAssembly::LOOP:
1536-
assert(Stack.back() == &MBB && "Loop top should be balanced");
1570+
assert(Stack.back().first == &MBB && "Loop top should be balanced");
15371571
Stack.pop_back();
1538-
DelegateStack.pop_back();
15391572
break;
15401573

15411574
case WebAssembly::END_BLOCK:
1542-
Stack.push_back(&MBB);
1543-
DelegateStack.push_back(&MBB);
1575+
Stack.push_back(std::make_pair(&MBB, &MI));
15441576
break;
15451577

15461578
case WebAssembly::END_TRY:
15471579
// We handle DELEGATE in the default level, because DELEGATE has
1548-
// immediate operands to rewirte.
1549-
Stack.push_back(&MBB);
1580+
// immediate operands to rewrite.
1581+
Stack.push_back(std::make_pair(&MBB, &MI));
15501582
break;
15511583

15521584
case WebAssembly::END_LOOP:
1553-
Stack.push_back(EndToBegin[&MI]->getParent());
1554-
DelegateStack.push_back(EndToBegin[&MI]->getParent());
1555-
break;
1556-
1557-
case WebAssembly::CATCH:
1558-
case WebAssembly::CATCH_ALL:
1559-
DelegateStack.push_back(&MBB);
1585+
Stack.push_back(std::make_pair(EndToBegin[&MI]->getParent(), &MI));
15601586
break;
15611587

15621588
default:
@@ -1569,18 +1595,17 @@ void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) {
15691595
if (MO.isMBB()) {
15701596
if (MI.getOpcode() == WebAssembly::DELEGATE)
15711597
MO = MachineOperand::CreateImm(
1572-
getDepth(DelegateStack, MO.getMBB()));
1598+
getDelegateDepth(Stack, MO.getMBB()));
15731599
else
1574-
MO = MachineOperand::CreateImm(getDepth(Stack, MO.getMBB()));
1600+
MO = MachineOperand::CreateImm(
1601+
getBranchDepth(Stack, MO.getMBB()));
15751602
}
15761603
MI.addOperand(MF, MO);
15771604
}
15781605
}
15791606

1580-
if (MI.getOpcode() == WebAssembly::DELEGATE) {
1581-
Stack.push_back(&MBB);
1582-
DelegateStack.push_back(&MBB);
1583-
}
1607+
if (MI.getOpcode() == WebAssembly::DELEGATE)
1608+
Stack.push_back(std::make_pair(&MBB, &MI));
15841609
break;
15851610
}
15861611
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ try.cont: ; preds = %catch.start0
658658
; --- try-delegate starts (call unwind mismatch)
659659
; NOSORT: try
660660
; NOSORT: call __cxa_end_catch
661-
; NOSORT: delegate 2 # label/catch{{[0-9]+}}: to caller
661+
; NOSORT: delegate 3 # label/catch{{[0-9]+}}: to caller
662662
; --- try-delegate ends (call unwind mismatch)
663663
; NOSORT: end_try
664664
; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller

0 commit comments

Comments
 (0)