Index: include/llvm/CodeGen/Passes.h =================================================================== --- include/llvm/CodeGen/Passes.h +++ include/llvm/CodeGen/Passes.h @@ -606,6 +606,10 @@ /// \brief This pass lays out funclets contiguously. extern char &FuncletLayoutID; + /// This pass inserts the XRay instrumentation sleds if they are supported by + /// the target platform. + extern char &XRayInstrumentationID; + /// \brief This pass implements the "patchable-function" attribute. extern char &PatchableFunctionID; Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -335,6 +335,7 @@ void initializeLoopVersioningPassPass(PassRegistry &); void initializeWholeProgramDevirtPass(PassRegistry &); void initializePatchableFunctionPass(PassRegistry &); +void initializeXRayInstrumentationPass(PassRegistry &); } #endif Index: include/llvm/MC/MCObjectFileInfo.h =================================================================== --- include/llvm/MC/MCObjectFileInfo.h +++ include/llvm/MC/MCObjectFileInfo.h @@ -193,6 +193,9 @@ MCSection *XDataSection; MCSection *SXDataSection; + /// XRay specific section. + MCSection *XRaySection; + public: void InitMCObjectFileInfo(const Triple &TT, Reloc::Model RM, CodeModel::Model CM, MCContext &ctx); @@ -344,6 +347,9 @@ MCSection *getXDataSection() const { return XDataSection; } MCSection *getSXDataSection() const { return SXDataSection; } + // XRay specific sections. + MCSection *getXRaySection() const { return XRaySection; } + MCSection *getEHFrameSection() { return EHFrameSection; } Index: include/llvm/Target/Target.td =================================================================== --- include/llvm/Target/Target.td +++ include/llvm/Target/Target.td @@ -939,6 +939,30 @@ let mayStore = 1; let hasSideEffects = 1; } +def PATCHABLE_FUNCTION_ENTER : Instruction { + let OutOperandList = (outs); + let InOperandList = (ins); + let AsmString = "# XRay Function Enter."; + let usesCustomInserter = 1; + let hasSideEffects = 0; +} +def PATCHABLE_RET : Instruction { + let OutOperandList = (outs unknown:$dst); + let InOperandList = (ins variable_ops); + let AsmString = "# XRay Function Exit."; + let usesCustomInserter = 1; + let hasSideEffects = 1; + let isReturn = 1; +} +def PATCHABLE_TAIL_CALL : Instruction { + let OutOperandList = (outs); + let InOperandList = (ins); + let AsmString = "# XRay Tail Call Exit."; + let usesCustomInserter = 1; + let hasSideEffects = 0; + let isTerminator = 1; + let isReturn = 1; +} // Generic opcodes used in GlobalISel. include "llvm/Target/GenericOpcodes.td" Index: include/llvm/Target/TargetOpcodes.def =================================================================== --- include/llvm/Target/TargetOpcodes.def +++ include/llvm/Target/TargetOpcodes.def @@ -142,16 +142,25 @@ /// original instruction. HANDLE_TARGET_OPCODE(PATCHABLE_OP, 23) +/// PATCHABLE_FUNCTION_ENTER - a marker for XRay function entry blocks. +HANDLE_TARGET_OPCODE(PATCHABLE_FUNCTION_ENTER, 24) + +/// PATCHABLE_RET - a marker for XRay function exit blocks. +HANDLE_TARGET_OPCODE(PATCHABLE_RET, 25) + +/// PATCHABLE_TAIL_CALL - a marker for XRay tail call exits. +HANDLE_TARGET_OPCODE(PATCHABLE_TAIL_CALL, 26) + /// The following generic opcodes are not supposed to appear after ISel. /// This is something we might want to relax, but for now, this is convenient /// to produce diagnostics. /// Generic ADD instruction. This is an integer add. -HANDLE_TARGET_OPCODE(G_ADD, 24) +HANDLE_TARGET_OPCODE(G_ADD, 27) HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_START, G_ADD) /// Generic BRANCH instruction. This is an unconditional branch. -HANDLE_TARGET_OPCODE(G_BR, 25) +HANDLE_TARGET_OPCODE(G_BR, 28) // TODO: Add more generic opcodes as we move along. Index: lib/CodeGen/CMakeLists.txt =================================================================== --- lib/CodeGen/CMakeLists.txt +++ lib/CodeGen/CMakeLists.txt @@ -130,6 +130,7 @@ UnreachableBlockElim.cpp VirtRegMap.cpp WinEHPrepare.cpp + XRayInstrumentation.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/CodeGen Index: lib/CodeGen/CodeGen.cpp =================================================================== --- lib/CodeGen/CodeGen.cpp +++ lib/CodeGen/CodeGen.cpp @@ -56,6 +56,7 @@ initializeMachineSchedulerPass(Registry); initializeMachineSinkingPass(Registry); initializeMachineVerifierPassPass(Registry); + initializeXRayInstrumentationPass(Registry); initializePatchableFunctionPass(Registry); initializeOptimizePHIsPass(Registry); initializePEIPass(Registry); Index: lib/CodeGen/Passes.cpp =================================================================== --- lib/CodeGen/Passes.cpp +++ lib/CodeGen/Passes.cpp @@ -602,6 +602,7 @@ addPass(&StackMapLivenessID, false); addPass(&LiveDebugValuesID, false); + addPass(&XRayInstrumentationID, false); addPass(&PatchableFunctionID, false); AddingMachinePasses = false; Index: lib/CodeGen/XRayInstrumentation.cpp =================================================================== --- /dev/null +++ lib/CodeGen/XRayInstrumentation.cpp @@ -0,0 +1,96 @@ +//===-- XRayInstrumentation.cpp - Adds XRay instrumentation to functions. -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a MachineFunctionPass that inserts the appropriate +// XRay instrumentation instructions. We look for XRay-specific attributes +// on the function to determine whether we should insert the replacement +// operations. +// +//===---------------------------------------------------------------------===// + +#include "llvm/CodeGen/Analysis.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Target/TargetInstrInfo.h" +#include "llvm/Target/TargetSubtargetInfo.h" + +using namespace llvm; + +namespace { +struct XRayInstrumentation : public MachineFunctionPass { + static char ID; + + XRayInstrumentation() : MachineFunctionPass(ID) { + initializeXRayInstrumentationPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} + +bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) { + auto &F = *MF.getFunction(); + auto InstrAttr = F.getFnAttribute("function-instrument"); + bool AlwaysInstrument = !InstrAttr.hasAttribute(Attribute::None) && + InstrAttr.isStringAttribute() && + InstrAttr.getValueAsString() == "xray-always"; + Attribute Attr = F.getFnAttribute("xray-instruction-threshold"); + unsigned XRayThreshold = 0; + if (!AlwaysInstrument) { + if (Attr.hasAttribute(Attribute::None) || !Attr.isStringAttribute()) + return false; // XRay threshold attribute not found. + if (Attr.getValueAsString().getAsInteger(10, XRayThreshold)) + return false; // Invalid value for threshold. + if (F.size() < XRayThreshold) + return false; // Function is too small. + } + + // FIXME: Do the loop triviality analysis here or in an earlier pass. + + // First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the + // MachineFunction. + auto &FirstMBB = *MF.begin(); + auto &FirstMI = *FirstMBB.begin(); + auto *TII = MF.getSubtarget().getInstrInfo(); + BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(), + TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER)); + + // Then we look for *all* terminators and returns, then replace those with + // PATCHABLE_RET instructions. + SmallVector Terminators; + for (auto &MBB : MF) { + for (auto &Terminator : MBB.terminators()) { + // FIXME: Handle tail calls here too? + if (Terminator.isReturn()) { + // Replace return instructions with: + // PATCHABLE_RET , ... + auto MIB = BuildMI(MBB, Terminator, Terminator.getDebugLoc(), + TII->get(TargetOpcode::PATCHABLE_RET)) + .addImm(Terminator.getOpcode()); + for (auto &MO : Terminator.operands()) { + if (MO.isReg()) + MIB.addReg(MO.getReg(), RegState::Implicit); + } + Terminators.push_back(&Terminator); + } + } + } + + for (auto &I : Terminators) + I->eraseFromParent(); + + return true; +} + +char XRayInstrumentation::ID = 0; +char &llvm::XRayInstrumentationID = XRayInstrumentation::ID; +INITIALIZE_PASS(XRayInstrumentation, "xray-instrumentation", "Insert XRay ops", + false, false); Index: lib/MC/MCObjectFileInfo.cpp =================================================================== --- lib/MC/MCObjectFileInfo.cpp +++ lib/MC/MCObjectFileInfo.cpp @@ -281,6 +281,9 @@ 0, SectionKind::getMetadata()); TLSExtraDataSection = TLSTLVSection; + + XRaySection = Ctx->getMachOSection("__XRAY_INSTR_MAP", "__xray_instr_map", 0, + SectionKind::getMetadata()); } void MCObjectFileInfo::initELFMCObjectFileInfo(Triple T) { @@ -584,6 +587,9 @@ EHFrameSection = Ctx->getELFSection(".eh_frame", EHSectionType, EHSectionFlags); + + XRaySection = + Ctx->getELFSection(".xray_instr_map", ELF::SHT_PROGBITS, ELF::SHF_ALLOC); } void MCObjectFileInfo::initCOFFMCObjectFileInfo(Triple T) { Index: lib/Target/X86/X86AsmPrinter.h =================================================================== --- lib/Target/X86/X86AsmPrinter.h +++ lib/Target/X86/X86AsmPrinter.h @@ -71,6 +71,26 @@ StackMapShadowTracker SMShadowTracker; + // This describes the kind of sled we're storing in the XRay table. + enum class SledKind : uint8_t { + FUNCTION_ENTER, + FUNCTION_EXIT, + TAIL_CALL, + }; + + // The table will contain these structs that point to the sled, the function + // containing the sled, and what kind of sled (and whether they should always + // be instrumented). + struct XRayFunctionEntry { + const MCSymbol *Sled; + const MCSymbol *Function; + SledKind Kind; + bool AlwaysInstrument; + }; + + // All the sleds to be emitted. + std::vector Sleds; + // All instructions emitted by the X86AsmPrinter should use this helper // method. // @@ -86,10 +106,21 @@ void LowerTlsAddr(X86MCInstLower &MCInstLowering, const MachineInstr &MI); - public: - explicit X86AsmPrinter(TargetMachine &TM, - std::unique_ptr Streamer) - : AsmPrinter(TM, std::move(Streamer)), SM(*this), FM(*this) {} + // XRay-specific lowering for X86. + void LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI, + X86MCInstLower &MCIL); + void LowerPATCHABLE_RET(const MachineInstr &MI, X86MCInstLower &MCIL); + void LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI, X86MCInstLower &MCIL); + + // Helper function that emits all XRay sleds we've collected. + void EmitXRayTable(); + + // Helper function to record a given XRay sled. + void recordSled(MCSymbol *Sled, const MachineInstr &MI); +public: + explicit X86AsmPrinter(TargetMachine &TM, + std::unique_ptr Streamer) + : AsmPrinter(TM, std::move(Streamer)), SM(*this), FM(*this) {} const char *getPassName() const override { return "X86 Assembly / Object Emitter"; Index: lib/Target/X86/X86AsmPrinter.cpp =================================================================== --- lib/Target/X86/X86AsmPrinter.cpp +++ lib/Target/X86/X86AsmPrinter.cpp @@ -658,6 +658,7 @@ SM.serializeToStackMapSection(); FM.serializeToFaultMapSection(); + EmitXRayTable(); // Funny Darwin hack: This flag tells the linker that no global symbols // contain code that falls through to other global symbols (e.g. the obvious @@ -702,6 +703,7 @@ if (TT.isOSBinFormatELF()) { SM.serializeToStackMapSection(); FM.serializeToFaultMapSection(); + EmitXRayTable(); } } Index: lib/Target/X86/X86FrameLowering.cpp =================================================================== --- lib/Target/X86/X86FrameLowering.cpp +++ lib/Target/X86/X86FrameLowering.cpp @@ -159,6 +159,7 @@ unsigned Opc = MBBI->getOpcode(); switch (Opc) { default: return 0; + case TargetOpcode::PATCHABLE_RET: case X86::RET: case X86::RETL: case X86::RETQ: Index: lib/Target/X86/X86MCInstLower.cpp =================================================================== --- lib/Target/X86/X86MCInstLower.cpp +++ lib/Target/X86/X86MCInstLower.cpp @@ -36,9 +36,12 @@ #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" +#include "llvm/MC/MCSection.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetLoweringObjectFile.h" + using namespace llvm; namespace { @@ -1059,6 +1062,99 @@ getSubtargetInfo()); } +void X86AsmPrinter::recordSled(MCSymbol *Sled, const MachineInstr &MI) { + auto Fn = MI.getParent()->getParent()->getFunction(); + auto Attr = Fn->getFnAttribute("function-instrument"); + bool AlwaysInstrument = false; + if (!Attr.hasAttribute(Attribute::None) && Attr.isStringAttribute() && + Attr.getValueAsString() == "xray-always") + AlwaysInstrument = true; + Sleds.push_back( + {Sled, CurrentFnSym, SledKind::FUNCTION_ENTER, AlwaysInstrument}); +} + +void X86AsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI, + X86MCInstLower &MCIL) { + // We want to emit the following pattern: + // + // .Lxray_sled_N: + // .palign 2, ... + // jmp .tmpN + // # 9 bytes worth of noops + // .tmpN + // + auto CurSled = OutContext.createTempSymbol("xray_sled_", true); + OutStreamer->EmitLabel(CurSled); + OutStreamer->EmitCodeAlignment(4); + auto Target = OutContext.createLinkerPrivateTempSymbol(); + OutStreamer->EmitInstruction( + MCInstBuilder(X86::JMP_1) + .addExpr(MCSymbolRefExpr::create(Target, OutContext)), + getSubtargetInfo()); + EmitNops(*OutStreamer, 9, Subtarget->is64Bit(), getSubtargetInfo()); + OutStreamer->EmitLabel(Target); + recordSled(CurSled, MI); +} + +void X86AsmPrinter::LowerPATCHABLE_RET(const MachineInstr &MI, + X86MCInstLower &MCIL) { + // Since PATCHABLE_RET takes the opcode of the return statement as an + // argument, we use that to emit the correct form of the RET that we want. + // i.e. when we see this: + // + // PATCHABLE_RET X86::RET ... + // + // We should emit the RET followed by sleds. + // + // .Lxray_sled_N: + // ret # or equivalent instruction + // # 10 bytes worth of noops + // + // This just makes sure that the alignment for the next instruction is 2. + auto CurSled = OutContext.createTempSymbol("xray_sled_", true); + OutStreamer->EmitLabel(CurSled); + unsigned OpCode = MI.getOperand(0).getImm(); + MCInst Ret; + Ret.setOpcode(OpCode); + for (auto &MO : make_range(MI.operands_begin() + 1, MI.operands_end())) + if (auto MaybeOperand = MCIL.LowerMachineOperand(&MI, MO)) + Ret.addOperand(MaybeOperand.getValue()); + OutStreamer->EmitInstruction(Ret, getSubtargetInfo()); + EmitNops(*OutStreamer, 10, Subtarget->is64Bit(), getSubtargetInfo()); + recordSled(CurSled, MI); +} + +void X86AsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI, + X86MCInstLower &MCIL) { + // FIXME: Do something later. +} + +void X86AsmPrinter::EmitXRayTable() { + // Do not emit the XRay section if there were no sleds encountered. + if (Sleds.empty()) return; + + MCSection *XRaySection = getObjFileLowering().getXRaySection(); + assert(XRaySection && "There should be an XRay Section!"); + auto PrevSection = OutStreamer->getCurrentSectionOnly(); + OutStreamer->SwitchSection(XRaySection); + + // We lay it out so that we have fixed-sized records, where we emit a label + // reference (8 bytes), then a byte for the kind of entry, another byte for + // whether to always instrument, and a 64-bit (8 byte) value. + for (const auto &Sled : Sleds) { + OutStreamer->EmitSymbolValue(Sled.Sled, 8); + OutStreamer->EmitSymbolValue(Sled.Function, 8); + uint8_t Kind = static_cast(Sled.Kind); + OutStreamer->EmitBytes(StringRef(reinterpret_cast(&Kind), 1)); + uint8_t AlwaysInstrument = Sled.AlwaysInstrument ? 1 : 0; + OutStreamer->EmitBytes( + StringRef(reinterpret_cast(&AlwaysInstrument), 1)); + } + + // Switch back to wherever we were. + OutStreamer->SwitchSection(PrevSection); +} + // Returns instruction preceding MBBI in MachineFunction. // If MBBI is the first instruction of the first basic block, returns null. static MachineBasicBlock::const_iterator @@ -1299,6 +1395,15 @@ case TargetOpcode::PATCHPOINT: return LowerPATCHPOINT(*MI, MCInstLowering); + case TargetOpcode::PATCHABLE_FUNCTION_ENTER: + return LowerPATCHABLE_FUNCTION_ENTER(*MI, MCInstLowering); + + case TargetOpcode::PATCHABLE_RET: + return LowerPATCHABLE_RET(*MI, MCInstLowering); + + case TargetOpcode::PATCHABLE_TAIL_CALL: + return LowerPATCHABLE_TAIL_CALL(*MI, MCInstLowering); + case X86::MORESTACK_RET: EmitAndCountInstruction(MCInstBuilder(getRetOpcode(*Subtarget))); return; Index: test/CodeGen/X86/xray-attribute-instrumentation.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/xray-attribute-instrumentation.ll @@ -0,0 +1,13 @@ +; RUN: llc -filetype=asm -o - -mtriple=x86_64-apple-macosx < %s | FileCheck %s + +define i32 @foo() nounwind noinline uwtable "function-instrument"="xray-always" { +; CHECK-LABEL: Lxray_sled_0: +; CHECK-NEXT: .p2align 2, 0x90 +; CHECK-NEXT: jmp ltmp0 +; CHECK-NEXT: nopw 512(%rax,%rax) +; CHECK-LABEL: ltmp0: + ret i32 0 +; CHECK-LABEL: Lxray_sled_1: +; CHECK-NEXT: retq +; CHECK-NEXT: nopw %cs:512(%rax,%rax) +} Index: test/CodeGen/X86/xray-selective-instrumentation-miss.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/xray-selective-instrumentation-miss.ll @@ -0,0 +1,9 @@ +; RUN: llc -mcpu=nehalem < %s | not grep xray_sled_ + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128" +target triple = "x86_64-apple-darwin8" + +define i32 @foo() nounwind uwtable "xray-instruction-threshold"="3" { +entry: + ret i32 0 +} Index: test/CodeGen/X86/xray-selective-instrumentation.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/xray-selective-instrumentation.ll @@ -0,0 +1,9 @@ +; RUN: llc -mcpu=nehalem < %s | grep xray_sled_ + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128" +target triple = "x86_64-apple-darwin8" + +define i32 @foo() nounwind uwtable "xray-instruction-threshold"="1" { +entry: + ret i32 0 +} Index: test/TableGen/trydecode-emission.td =================================================================== --- test/TableGen/trydecode-emission.td +++ test/TableGen/trydecode-emission.td @@ -36,8 +36,8 @@ // CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, // Inst{7-4} ... // CHECK-NEXT: /* 3 */ MCD::OPC_FilterValue, 0, 14, 0, // Skip to: 21 // CHECK-NEXT: /* 7 */ MCD::OPC_CheckField, 2, 2, 0, 5, 0, // Skip to: 18 -// CHECK-NEXT: /* 13 */ MCD::OPC_TryDecode, 27, 0, 0, 0, // Opcode: InstB, skip to: 18 -// CHECK-NEXT: /* 18 */ MCD::OPC_Decode, 26, 1, // Opcode: InstA +// CHECK-NEXT: /* 13 */ MCD::OPC_TryDecode, 30, 0, 0, 0, // Opcode: InstB, skip to: 18 +// CHECK-NEXT: /* 18 */ MCD::OPC_Decode, 29, 1, // Opcode: InstA // CHECK-NEXT: /* 21 */ MCD::OPC_Fail, // CHECK: if (DecodeInstB(MI, insn, Address, Decoder) == MCDisassembler::Fail) { DecodeComplete = false; return MCDisassembler::Fail; } Index: test/TableGen/trydecode-emission2.td =================================================================== --- test/TableGen/trydecode-emission2.td +++ test/TableGen/trydecode-emission2.td @@ -35,9 +35,9 @@ // CHECK-NEXT: /* 7 */ MCD::OPC_ExtractField, 5, 3, // Inst{7-5} ... // CHECK-NEXT: /* 10 */ MCD::OPC_FilterValue, 0, 22, 0, // Skip to: 36 // CHECK-NEXT: /* 14 */ MCD::OPC_CheckField, 0, 2, 3, 5, 0, // Skip to: 25 -// CHECK-NEXT: /* 20 */ MCD::OPC_TryDecode, 27, 0, 0, 0, // Opcode: InstB, skip to: 25 +// CHECK-NEXT: /* 20 */ MCD::OPC_TryDecode, 30, 0, 0, 0, // Opcode: InstB, skip to: 25 // CHECK-NEXT: /* 25 */ MCD::OPC_CheckField, 3, 2, 0, 5, 0, // Skip to: 36 -// CHECK-NEXT: /* 31 */ MCD::OPC_TryDecode, 26, 1, 0, 0, // Opcode: InstA, skip to: 36 +// CHECK-NEXT: /* 31 */ MCD::OPC_TryDecode, 29, 1, 0, 0, // Opcode: InstA, skip to: 36 // CHECK-NEXT: /* 36 */ MCD::OPC_Fail, // CHECK: if (DecodeInstB(MI, insn, Address, Decoder) == MCDisassembler::Fail) { DecodeComplete = false; return MCDisassembler::Fail; } Index: test/TableGen/trydecode-emission3.td =================================================================== --- test/TableGen/trydecode-emission3.td +++ test/TableGen/trydecode-emission3.td @@ -37,8 +37,8 @@ // CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, // Inst{7-4} ... // CHECK-NEXT: /* 3 */ MCD::OPC_FilterValue, 0, 14, 0, // Skip to: 21 // CHECK-NEXT: /* 7 */ MCD::OPC_CheckField, 2, 2, 0, 5, 0, // Skip to: 18 -// CHECK-NEXT: /* 13 */ MCD::OPC_TryDecode, 27, 0, 0, 0, // Opcode: InstB, skip to: 18 -// CHECK-NEXT: /* 18 */ MCD::OPC_Decode, 26, 1, // Opcode: InstA +// CHECK-NEXT: /* 13 */ MCD::OPC_TryDecode, 30, 0, 0, 0, // Opcode: InstB, skip to: 18 +// CHECK-NEXT: /* 18 */ MCD::OPC_Decode, 29, 1, // Opcode: InstA // CHECK-NEXT: /* 21 */ MCD::OPC_Fail, // CHECK: if (DecodeInstBOp(MI, tmp, Address, Decoder) == MCDisassembler::Fail) { DecodeComplete = false; return MCDisassembler::Fail; }