Index: lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h =================================================================== --- lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h +++ lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h @@ -25,8 +25,13 @@ class MCSubtargetInfo; class WebAssemblyInstPrinter final : public MCInstPrinter { - uint64_t ControlFlowCounter; - SmallVector, 0> ControlFlowStack; + uint64_t ControlFlowCounter = 0; + uint64_t EHPadStackCounter = 0; + SmallVector, 4> ControlFlowStack; + SmallVector EHPadStack; + + enum EHInstKind { TRY, CATCH, END_TRY }; + EHInstKind LastSeenEHInst = END_TRY; public: WebAssemblyInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, Index: lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp =================================================================== --- lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp +++ lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp @@ -35,7 +35,7 @@ WebAssemblyInstPrinter::WebAssemblyInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, const MCRegisterInfo &MRI) - : MCInstPrinter(MAI, MII, MRI), ControlFlowCounter(0) {} + : MCInstPrinter(MAI, MII, MRI) {} void WebAssemblyInstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { @@ -70,32 +70,69 @@ if (CommentStream) { // Observe any effects on the control flow stack, for use in annotating // control flow label references. - switch (MI->getOpcode()) { + unsigned Opc = MI->getOpcode(); + switch (Opc) { default: break; + case WebAssembly::LOOP: - case WebAssembly::LOOP_S: { + case WebAssembly::LOOP_S: printAnnotation(OS, "label" + utostr(ControlFlowCounter) + ':'); ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, true)); break; - } + case WebAssembly::BLOCK: case WebAssembly::BLOCK_S: ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false)); break; + + case WebAssembly::TRY: + case WebAssembly::TRY_S: + ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false)); + EHPadStack.push_back(EHPadStackCounter++); + LastSeenEHInst = TRY; + break; + case WebAssembly::END_LOOP: case WebAssembly::END_LOOP_S: if (ControlFlowStack.empty()) report_fatal_error("End marker mismatch!"); ControlFlowStack.pop_back(); break; + case WebAssembly::END_BLOCK: case WebAssembly::END_BLOCK_S: if (ControlFlowStack.empty()) - report_fatal_error("END marker mismatch!"); + report_fatal_error("End marker mismatch!"); printAnnotation( OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':'); break; + + case WebAssembly::END_TRY: + case WebAssembly::END_TRY_S: + if (ControlFlowStack.empty()) + report_fatal_error("End marker mismatch!"); + printAnnotation( + OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':'); + LastSeenEHInst = END_TRY; + break; + + case WebAssembly::CATCH_I32: + case WebAssembly::CATCH_I32_S: + case WebAssembly::CATCH_I64: + case WebAssembly::CATCH_I64_S: + case WebAssembly::CATCH_ALL: + case WebAssembly::CATCH_ALL_S: + assert(LastSeenEHInst != END_TRY); + // There can be multiple catch instructions for one try instruction, so we + // only print 'catch' label when the last seen EH instruction was 'try'. + if (LastSeenEHInst == TRY) { + if (EHPadStack.empty()) + report_fatal_error("try-catch mismatch!"); + printAnnotation(OS, "catch" + utostr(EHPadStack.pop_back_val()) + ':'); + } + LastSeenEHInst = CATCH; + break; } // Annotate any control flow label references. @@ -110,9 +147,28 @@ uint64_t Depth = MI->getOperand(i).getImm(); if (!Printed.insert(Depth).second) continue; - const auto &Pair = ControlFlowStack.rbegin()[Depth]; - printAnnotation(OS, utostr(Depth) + ": " + (Pair.second ? "up" : "down") + - " to label" + utostr(Pair.first)); + + if (Opc == WebAssembly::RETHROW || Opc == WebAssembly::RETHROW_S) { + if (Depth > EHPadStack.size()) + report_fatal_error("Invalid depth argument: " + utostr(Depth)); + if (Depth == EHPadStack.size()) { + // This can happen when rethrow instruction breaks out of all nests + // and throws up to the current function's caller. + printAnnotation(OS, utostr(Depth) + ": " + "to caller"); + } else { + uint64_t CatchNo = EHPadStack.rbegin()[Depth]; + printAnnotation(OS, utostr(Depth) + ": " + "down to catch" + + utostr(CatchNo)); + } + + } else { + if (Depth >= ControlFlowStack.size()) + report_fatal_error("Invalid depth argument: " + utostr(Depth)); + const auto &Pair = ControlFlowStack.rbegin()[Depth]; + printAnnotation(OS, utostr(Depth) + ": " + + (Pair.second ? "up" : "down") + " to label" + + utostr(Pair.first)); + } } } } Index: test/CodeGen/WebAssembly/annotations.mir =================================================================== --- /dev/null +++ test/CodeGen/WebAssembly/annotations.mir @@ -0,0 +1,94 @@ +# RUN: llc -mtriple=wasm32-unknown-unknown -start-after xray-instrumentation -wasm-keep-registers %s -o - | FileCheck %s + +--- +# Tests if block/loop/try/catch/end instructions are correctly printed with +# their annotations. + +# CHECK: test0: +# CHECK: block +# CHECK: try +# CHECK: br 0 # 0: down to label1 +# CHECK: catch_all # catch0: +# CHECK: block +# CHECK: br_if 0, 1 # 0: down to label2 +# CHECK: loop # label3: +# CHECK: br_if 0, 1 # 0: up to label3 +# CHECK: end_loop +# CHECK: end_block # label2: +# CHECK: try +# CHECK: rethrow 0 # 0: down to catch1 +# CHECK: catch_all # catch1: +# CHECK: block +# CHECK: try +# CHECK: br 0 # 0: down to label6 +# CHECK: catch_all # catch2: +# CHECK: unreachable +# CHECK: end_try # label6: +# CHECK: end_block # label5: +# CHECK: rethrow 0 # 0: to caller +# CHECK: end_try # label4: +# CHECK: end_try # label1: +# CHECK: end_block # label0: + +name: test0 +liveins: + - { reg: '$arguments', reg: '$value_stack' } +body: | + bb.0: + successors: %bb.7, %bb.1 + BLOCK 64, implicit-def $value_stack, implicit $value_stack + TRY 64, implicit-def $value_stack, implicit $value_stack + BR 0, implicit-def $arguments + + bb.1 (landing-pad): + ; predecessors: %bb.0 + successors: %bb.2, %bb.3 + + CATCH_ALL implicit-def $arguments + BLOCK 64, implicit-def $value_stack, implicit $value_stack + BR_IF 0, 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.2: + ; predecessors: %bb.1, %bb.2 + successors: %bb.2, %bb.3 + + LOOP 64, implicit-def $value_stack, implicit $value_stack + BR_IF 0, 1, implicit-def $arguments + + bb.3: + ; predecessors: %bb.1, %bb.2 + successors: %bb.4 + + END_LOOP implicit-def $value_stack, implicit $value_stack + END_BLOCK implicit-def $value_stack, implicit $value_stack + TRY 64, implicit-def $value_stack, implicit $value_stack + RETHROW 0, implicit-def $arguments + + bb.4 (landing-pad): + ; predecessors: %bb.3 + successors: %bb.6, %bb.5 + + CATCH_ALL implicit-def $arguments + BLOCK 64, implicit-def $value_stack, implicit $value_stack + TRY 64, implicit-def $value_stack, implicit $value_stack + BR 0, implicit-def $arguments + + bb.5 (landing-pad): + ; predecessors: %bb.4 + CATCH_ALL implicit-def $arguments + UNREACHABLE implicit-def dead $arguments + + bb.6: + ; predecessors: %bb.4 + END_TRY implicit-def $value_stack, implicit $value_stack + END_BLOCK implicit-def $value_stack, implicit $value_stack + RETHROW 0, implicit-def $arguments + + bb.7: + ; predecessors: %bb.0 + END_TRY implicit-def $value_stack, implicit $value_stack + END_TRY implicit-def $value_stack, implicit $value_stack + END_BLOCK implicit-def $value_stack, implicit $value_stack + FALLTHROUGH_RETURN_VOID implicit-def dead $arguments + END_FUNCTION implicit-def $value_stack, implicit $value_stack +...