[WebAssembly] Update InstPrinter and AsmParser for new EH instructions

This updates InstPrinter and AsmParser for `delegate` and `catch_all`
instructions. Both will reject programs with multiple `catch_all`s per a
single `try`. And InstPrinter uses `EHInstStack` to figure out whether
to print catch label comments: It does not print catch label comments
for second `catch` or `catch_all` in a `try`.

Reviewed By: aardappel

Differential Revision: https://reviews.llvm.org/D94051
This commit is contained in:
Heejin Ahn 2021-01-01 01:03:21 -08:00
parent 4af73572c7
commit 5afdd64a53
5 changed files with 53 additions and 8 deletions

View File

@ -216,6 +216,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
Block, Block,
Loop, Loop,
Try, Try,
CatchAll,
If, If,
Else, Else,
Undefined, Undefined,
@ -273,7 +274,9 @@ public:
case Loop: case Loop:
return {"loop", "end_loop"}; return {"loop", "end_loop"};
case Try: case Try:
return {"try", "end_try"}; return {"try", "end_try/delegate"};
case CatchAll:
return {"catch_all", "end_try"};
case If: case If:
return {"if", "end_if"}; return {"if", "end_if"};
case Else: case Else:
@ -533,10 +536,17 @@ public:
if (pop(Name, Try)) if (pop(Name, Try))
return true; return true;
push(Try); push(Try);
} else if (Name == "catch_all") {
if (pop(Name, Try))
return true;
push(CatchAll);
} else if (Name == "end_if") { } else if (Name == "end_if") {
if (pop(Name, If, Else)) if (pop(Name, If, Else))
return true; return true;
} else if (Name == "end_try") { } else if (Name == "end_try") {
if (pop(Name, Try, CatchAll))
return true;
} else if (Name == "delegate") {
if (pop(Name, Try)) if (pop(Name, Try))
return true; return true;
} else if (Name == "end_loop") { } else if (Name == "end_loop") {

View File

@ -108,6 +108,7 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
ControlFlowStack.push_back(std::make_pair(ControlFlowCounter, false)); ControlFlowStack.push_back(std::make_pair(ControlFlowCounter, false));
EHPadStack.push_back(ControlFlowCounter); EHPadStack.push_back(ControlFlowCounter);
DelegateStack.push_back(ControlFlowCounter++); DelegateStack.push_back(ControlFlowCounter++);
EHInstStack.push_back(TRY);
return; return;
case WebAssembly::END_LOOP: case WebAssembly::END_LOOP:
@ -133,11 +134,12 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
case WebAssembly::END_TRY: case WebAssembly::END_TRY:
case WebAssembly::END_TRY_S: case WebAssembly::END_TRY_S:
if (ControlFlowStack.empty()) { if (ControlFlowStack.empty() || EHInstStack.empty()) {
printAnnotation(OS, "End marker mismatch!"); printAnnotation(OS, "End marker mismatch!");
} else { } else {
printAnnotation( printAnnotation(
OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':'); OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
EHInstStack.pop_back();
} }
return; return;
@ -145,12 +147,27 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
case WebAssembly::CATCH_S: case WebAssembly::CATCH_S:
case WebAssembly::CATCH_ALL: case WebAssembly::CATCH_ALL:
case WebAssembly::CATCH_ALL_S: case WebAssembly::CATCH_ALL_S:
// There can be multiple catch instructions for one try instruction, so
// we print a label only for the first 'catch' label.
if (EHInstStack.empty()) {
printAnnotation(OS, "try-catch mismatch!");
} else if (EHInstStack.back() == CATCH_ALL) {
printAnnotation(OS, "catch/catch_all cannot occur after catch_all");
} else if (EHInstStack.back() == TRY) {
if (EHPadStack.empty() || DelegateStack.empty()) { if (EHPadStack.empty() || DelegateStack.empty()) {
printAnnotation(OS, "try-catch mismatch!"); printAnnotation(OS, "try-catch mismatch!");
} else { } else {
printAnnotation(OS, "catch" + utostr(EHPadStack.pop_back_val()) + ':'); printAnnotation(OS,
"catch" + utostr(EHPadStack.pop_back_val()) + ':');
DelegateStack.pop_back(); DelegateStack.pop_back();
} }
EHInstStack.pop_back();
if (Opc == WebAssembly::CATCH || Opc == WebAssembly::CATCH_S) {
EHInstStack.push_back(CATCH);
} else {
EHInstStack.push_back(CATCH_ALL);
}
}
return; return;
case WebAssembly::RETHROW: case WebAssembly::RETHROW:
@ -167,7 +184,7 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
case WebAssembly::DELEGATE: case WebAssembly::DELEGATE:
case WebAssembly::DELEGATE_S: case WebAssembly::DELEGATE_S:
if (ControlFlowStack.empty() || EHPadStack.empty() || if (ControlFlowStack.empty() || EHPadStack.empty() ||
DelegateStack.empty()) { DelegateStack.empty() || EHInstStack.empty()) {
printAnnotation(OS, "try-delegate mismatch!"); printAnnotation(OS, "try-delegate mismatch!");
} else { } else {
// 'delegate' is // 'delegate' is
@ -181,6 +198,7 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
": "; ": ";
EHPadStack.pop_back(); EHPadStack.pop_back();
DelegateStack.pop_back(); DelegateStack.pop_back();
EHInstStack.pop_back();
uint64_t Depth = MI->getOperand(0).getImm(); uint64_t Depth = MI->getOperand(0).getImm();
if (Depth >= DelegateStack.size()) { if (Depth >= DelegateStack.size()) {
Label += "to caller"; Label += "to caller";

View File

@ -33,6 +33,9 @@ class WebAssemblyInstPrinter final : public MCInstPrinter {
// separate stack for 'delegate'. // separate stack for 'delegate'.
SmallVector<uint64_t, 4> DelegateStack; SmallVector<uint64_t, 4> DelegateStack;
enum EHInstKind { TRY, CATCH, CATCH_ALL };
SmallVector<EHInstKind, 4> EHInstStack;
public: public:
WebAssemblyInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, WebAssemblyInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII,
const MCRegisterInfo &MRI); const MCRegisterInfo &MRI);

View File

@ -21,9 +21,12 @@ test_annotation:
try try
rethrow 0 rethrow 0
catch __cpp_exception catch __cpp_exception
catch_all
block block
try try
br 0 br 0
try
delegate 1
catch_all catch_all
end_try end_try
end_block end_block
@ -46,9 +49,12 @@ test_annotation:
# CHECK-NEXT: try # CHECK-NEXT: try
# CHECK-NEXT: rethrow 0 # down to catch3 # CHECK-NEXT: rethrow 0 # down to catch3
# CHECK-NEXT: catch __cpp_exception # catch3: # CHECK-NEXT: catch __cpp_exception # catch3:
# CHECK-NEXT: catch_all{{$}}
# CHECK-NEXT: block # CHECK-NEXT: block
# CHECK-NEXT: try # CHECK-NEXT: try
# CHECK-NEXT: br 0 # 0: down to label5 # CHECK-NEXT: br 0 # 0: down to label5
# CHECK-NEXT: try
# CHECK-NEXT: delegate 1 # label/catch6: down to catch4
# CHECK-NEXT: catch_all # catch5: # CHECK-NEXT: catch_all # catch5:
# CHECK-NEXT: end_try # label5: # CHECK-NEXT: end_try # label5:
# CHECK-NEXT: end_block # label4: # CHECK-NEXT: end_block # label4:

View File

@ -18,8 +18,16 @@ test0:
# CHECK: Block construct type mismatch, expected: end_block, instead got: end_if # CHECK: Block construct type mismatch, expected: end_block, instead got: end_if
end_if end_if
try try
# CHECK: Block construct type mismatch, expected: end_try/delegate, instead got: end_block
end_block
loop loop
# CHECK: Block construct type mismatch, expected: end_loop, instead got: end_function try
catch_all
catch_all
# CHECK: error: Block construct type mismatch, expected: end_try, instead got: catch_all
end
# CHECK: Block construct type mismatch, expected: end_try, instead got: end_function
# CHECK: error: Unmatched block construct(s) at function end: catch_all
# CHECK: error: Unmatched block construct(s) at function end: loop # CHECK: error: Unmatched block construct(s) at function end: loop
# CHECK: error: Unmatched block construct(s) at function end: try # CHECK: error: Unmatched block construct(s) at function end: try
# CHECK: error: Unmatched block construct(s) at function end: block # CHECK: error: Unmatched block construct(s) at function end: block