diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -24,6 +24,7 @@ #include "llvm/CodeGen/DwarfStringPoolEntry.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/StackMaps.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/IR/InlineAsm.h" #include "llvm/Support/ErrorHandling.h" #include @@ -766,6 +767,18 @@ /// Recursively emit Dwarf DIE tree. void emitDwarfDIE(const DIE &Die) const; + //===------------------------------------------------------------------===// + // CodeView Helper Routines + //===------------------------------------------------------------------===// + + /// Gets information required to create a CodeView debug symbol for a jump + /// table. + /// Return value is + virtual std::tuple + getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr, + const MCSymbol *BranchLabel) const; + //===------------------------------------------------------------------===// // Inline Asm Support //===------------------------------------------------------------------===// diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -1049,6 +1049,10 @@ /// is the jumptable index, the last one is the jumptable entry index. BR_JT, + /// JUMP_TABLE_DEBUG_INFO - Jumptable debug info. The first operand is the + /// chain, the second is the jumptable index. + JUMP_TABLE_DEBUG_INFO, + /// BRCOND - Conditional branch. The first operand is the chain, the /// second is the condition, the third is the block to branch to if the /// condition is true. If the type of the condition is not i1, then the diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h --- a/llvm/include/llvm/CodeGen/MachineInstr.h +++ b/llvm/include/llvm/CodeGen/MachineInstr.h @@ -1350,6 +1350,10 @@ return false; } + bool isJumpTableDebugInfo() const { + return getOpcode() == TargetOpcode::JUMP_TABLE_DEBUG_INFO; + } + bool isPHI() const { return getOpcode() == TargetOpcode::PHI || getOpcode() == TargetOpcode::G_PHI; diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -731,6 +731,7 @@ SDValue getTargetJumpTable(int JTI, EVT VT, unsigned TargetFlags = 0) { return getJumpTable(JTI, VT, true, TargetFlags); } + SDValue getJumpTableDebugInfo(int JTI, SDValue Chain, const SDLoc &DL); SDValue getConstantPool(const Constant *C, EVT VT, MaybeAlign Align = std::nullopt, int Offs = 0, bool isT = false, unsigned TargetFlags = 0); diff --git a/llvm/include/llvm/CodeGen/SelectionDAGISel.h b/llvm/include/llvm/CodeGen/SelectionDAGISel.h --- a/llvm/include/llvm/CodeGen/SelectionDAGISel.h +++ b/llvm/include/llvm/CodeGen/SelectionDAGISel.h @@ -328,6 +328,8 @@ void Select_STACKMAP(SDNode *N); void Select_PATCHPOINT(SDNode *N); + void Select_JUMP_TABLE_DEBUG_INFO(SDNode *N); + private: void DoInstructionSelection(); SDNode *MorphNode(SDNode *Node, unsigned TargetOpc, SDVTList VTList, diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -5304,11 +5304,10 @@ SelectionDAG &DAG) const; /// Expands target specific indirect branch for the case of JumpTable - /// expanasion. - virtual SDValue expandIndirectJTBranch(const SDLoc& dl, SDValue Value, SDValue Addr, - SelectionDAG &DAG) const { - return DAG.getNode(ISD::BRIND, dl, MVT::Other, Value, Addr); - } + /// expansion. + virtual SDValue expandIndirectJTBranch(const SDLoc &dl, SDValue Value, + SDValue Addr, int JTI, + SelectionDAG &DAG) const; // seteq(x, 0) -> truncate(srl(ctlz(zext(x)), log2(#bits))) // If we're comparing for equality to zero and isCtlzFast is true, expose the diff --git a/llvm/include/llvm/DebugInfo/CodeView/CodeView.h b/llvm/include/llvm/DebugInfo/CodeView/CodeView.h --- a/llvm/include/llvm/DebugInfo/CodeView/CodeView.h +++ b/llvm/include/llvm/DebugInfo/CodeView/CodeView.h @@ -615,6 +615,29 @@ return 1; return 4; } + +// Corresponds to CV_armswitchtype enum. +// This enum represents the different ways that jump tables entries can be +// encoded to represent the target address to jump to. +// * Pointer: The absolute address to jump to. +// * [U]Int[8|16|32]: A value that is added to some "base" address to get the +// address to jump to. +// * [U]Int[8|16]ShiftLeft: A value that is shifted left by an implementation +// specified amount, then added to some "base" address to get the address to +// jump to. +enum class JumpTableEntrySize : uint16_t { + Int8 = 0, + UInt8 = 1, + Int16 = 2, + UInt16 = 3, + Int32 = 4, + UInt32 = 5, + Pointer = 6, + UInt8ShiftLeft = 7, + UInt16ShiftLeft = 8, + Int8ShiftLeft = 9, + Int16ShiftLeft = 10, +}; } } diff --git a/llvm/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def b/llvm/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def --- a/llvm/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def +++ b/llvm/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def @@ -170,7 +170,6 @@ CV_SYMBOL(S_LOCAL_DPC_GROUPSHARED, 0x1154) CV_SYMBOL(S_DEFRANGE_DPC_PTR_TAG, 0x1157) CV_SYMBOL(S_DPC_SYM_TAG_MAP, 0x1158) -CV_SYMBOL(S_ARMSWITCHTABLE , 0x1159) CV_SYMBOL(S_POGODATA , 0x115c) CV_SYMBOL(S_INLINESITE2 , 0x115d) CV_SYMBOL(S_MOD_TYPEREF , 0x115f) @@ -231,6 +230,8 @@ SYMBOL_RECORD(S_HEAPALLOCSITE , 0x115e, HeapAllocationSiteSym) SYMBOL_RECORD(S_FRAMECOOKIE , 0x113a, FrameCookieSym) +SYMBOL_RECORD(S_ARMSWITCHTABLE, 0x1159, JumpTableSym) + SYMBOL_RECORD(S_CALLEES , 0x115a, CallerSym) SYMBOL_RECORD_ALIAS(S_CALLERS, 0x115b, CalleeSym, CallerSym) diff --git a/llvm/include/llvm/DebugInfo/CodeView/EnumTables.h b/llvm/include/llvm/DebugInfo/CodeView/EnumTables.h --- a/llvm/include/llvm/DebugInfo/CodeView/EnumTables.h +++ b/llvm/include/llvm/DebugInfo/CodeView/EnumTables.h @@ -48,6 +48,7 @@ ArrayRef> getCallingConventions(); ArrayRef> getFunctionOptionEnum(); ArrayRef> getLabelTypeEnum(); +ArrayRef> getJumpTableEntrySizeNames(); } // end namespace codeview } // end namespace llvm diff --git a/llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h b/llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h --- a/llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h +++ b/llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h @@ -144,6 +144,27 @@ uint32_t RecordOffset = 0; }; +class JumpTableSym : public SymbolRecord { +public: + explicit JumpTableSym(SymbolRecordKind Kind) : SymbolRecord(Kind) {} + JumpTableSym(uint32_t RecordOffset) + : SymbolRecord(SymbolRecordKind::JumpTableSym), + RecordOffset(RecordOffset) {} + + uint32_t BaseOffset = 0; + uint16_t BaseSegment = 0; + + JumpTableEntrySize SwitchType; + uint32_t BranchOffset = 0; + uint32_t TableOffset = 0; + uint16_t BranchSegment = 0; + uint16_t TableSegment = 0; + + uint32_t EntriesCount = 0; + + uint32_t RecordOffset = 0; +}; + class CallerSym : public SymbolRecord { public: explicit CallerSym(SymbolRecordKind Kind) : SymbolRecord(Kind) {} diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h @@ -225,6 +225,7 @@ Error visitKnownRecord(CVSymbol &Record, Thunk32Sym &Thunk) override; Error visitKnownRecord(CVSymbol &Record, UDTSym &UDT) override; Error visitKnownRecord(CVSymbol &Record, UsingNamespaceSym &UN) override; + Error visitKnownRecord(CVSymbol &Record, JumpTableSym &JumpTable) override; }; // Visitor for CodeView types and symbols to populate elements. diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def --- a/llvm/include/llvm/Support/TargetOpcodes.def +++ b/llvm/include/llvm/Support/TargetOpcodes.def @@ -226,6 +226,10 @@ // barrier, but does not correspond to any generated instruction. HANDLE_TARGET_OPCODE(MEMBARRIER) +// Provides information about what jump table the following indirect branch is +// using. +HANDLE_TARGET_OPCODE(JUMP_TABLE_DEBUG_INFO) + /// 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. diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -1410,6 +1410,14 @@ let Size = 0; let isMeta = true; } +def JUMP_TABLE_DEBUG_INFO : StandardPseudoInstruction { + let OutOperandList = (outs); + let InOperandList = (ins i64imm:$jti); + let AsmString = ""; + let hasSideEffects = false; + let Size = 0; + let isMeta = true; +} // Generic opcodes used in GlobalISel. include "llvm/Target/GenericOpcodes.td" diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td --- a/llvm/include/llvm/Target/TargetSelectionDAG.td +++ b/llvm/include/llvm/Target/TargetSelectionDAG.td @@ -650,6 +650,9 @@ def membarrier : SDNode<"ISD::MEMBARRIER", SDTNone, [SDNPHasChain, SDNPSideEffect]>; +def jump_table_debug_info : SDNode<"ISD::JUMP_TABLE_DEBUG_INFO", SDTNone, + [SDNPHasChain]>; + def atomic_fence : SDNode<"ISD::ATOMIC_FENCE" , SDTAtomicFence, [SDNPHasChain, SDNPSideEffect]>; diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -1726,6 +1726,10 @@ case TargetOpcode::MEMBARRIER: OutStreamer->emitRawComment("MEMBARRIER"); break; + case TargetOpcode::JUMP_TABLE_DEBUG_INFO: + // This instruction is only used to note jump table debug info, it's + // purely meta information. + break; default: emitInstruction(&MI); if (CanDoExtraAnalysis) { @@ -4166,3 +4170,18 @@ return dwarf::getUnitLengthFieldByteSize( OutStreamer->getContext().getDwarfFormat()); } + +std::tuple +AsmPrinter::getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr, + const MCSymbol *BranchLabel) const { + const auto TLI = MF->getSubtarget().getTargetLowering(); + const auto BaseExpr = + TLI->getPICJumpTableRelocBaseExpr(MF, JTI, MMI->getContext()); + const auto Base = &cast(BaseExpr)->getSymbol(); + + // By default, for the architectures that support CodeView, + // EK_LabelDifference32 is implemented as an Int32 from the base address. + return std::make_tuple(Base, 0, BranchLabel, + codeview::JumpTableEntrySize::Int32); +} diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -23,6 +23,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/DbgEntityHistoryCalculator.h" #include "llvm/CodeGen/DebugHandlerBase.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" @@ -133,6 +134,15 @@ StringRef Name; }; + struct JumpTableInfo { + codeview::JumpTableEntrySize EntrySize; + const MCSymbol *Base; + uint64_t BaseOffset; + const MCSymbol *Branch; + const MCSymbol *Table; + size_t TableSize; + }; + // For each function, store a vector of labels to its instructions, as well as // to the end of the function. struct FunctionInfo { @@ -160,6 +170,8 @@ std::vector> HeapAllocSites; + std::vector JumpTables; + const MCSymbol *Begin = nullptr; const MCSymbol *End = nullptr; unsigned FuncId = 0; @@ -478,6 +490,10 @@ unsigned getPointerSizeInBytes(); + void discoverJumpTableBranches(const MachineFunction *MF, bool isThumb); + void collectDebugInfoForJumpTables(const MachineFunction *MF, bool isThumb); + void emitDebugInfoForJumpTables(const FunctionInfo &FI); + protected: /// Gather pre-function debug information. void beginFunctionImpl(const MachineFunction *MF) override; diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -13,6 +13,7 @@ #include "CodeViewDebug.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/TinyPtrVector.h" @@ -26,6 +27,7 @@ #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/CodeGen/TargetLowering.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/Config/llvm-config.h" @@ -1243,6 +1245,8 @@ if (SP != nullptr) emitDebugInfoForUDTs(LocalUDTs); + emitDebugInfoForJumpTables(FI); + // We're done with this function. emitEndSymbolRecord(SymbolKind::S_PROC_ID_END); } @@ -1578,6 +1582,11 @@ } } } + + // Mark branches that may potentially be using jump tables with labels. + bool isThumb = Triple(MMI->getModule()->getTargetTriple()).getArch() == + llvm::Triple::ArchType::thumb; + discoverJumpTableBranches(MF, isThumb); } static bool shouldEmitUdt(const DIType *T) { @@ -3083,6 +3092,10 @@ } } + bool isThumb = Triple(MMI->getModule()->getTargetTriple()).getArch() == + llvm::Triple::ArchType::thumb; + collectDebugInfoForJumpTables(MF, isThumb); + CurFn->Annotations = MF->getCodeViewAnnotations(); CurFn->End = Asm->getFunctionEnd(); @@ -3442,3 +3455,135 @@ emitConstantSymbolRecord(DIGV->getType(), Value, QualifiedName); } } + +void forEachJumpTableBranch( + const MachineFunction *MF, bool isThumb, + const std::function &Callback) { + auto JTI = MF->getJumpTableInfo(); + if (JTI && !JTI->isEmpty()) { +#ifndef NDEBUG + auto UsedJTs = llvm::SmallBitVector(JTI->getJumpTables().size()); +#endif + for (const auto &MBB : *MF) { + // Search for indirect branches... + const auto LastMI = MBB.getFirstTerminator(); + if (LastMI != MBB.end() && LastMI->isIndirectBranch()) { + if (isThumb) { + // ... that directly use jump table operands. + // NOTE: ARM uses pattern matching to lower its BR_JT SDNode to + // machine instructions, hence inserting a JUMP_TABLE_DEBUG_INFO node + // interferes with this process *but* the resulting pseudo-instruction + // uses a Jump Table operand, so extract the jump table index directly + // from that. + for (const auto &MO : LastMI->operands()) { + if (MO.isJTI()) { + unsigned Index = MO.getIndex(); +#ifndef NDEBUG + UsedJTs.set(Index); +#endif + Callback(*JTI, *LastMI, Index); + break; + } + } + } else { + // ... that have jump table debug info. + // NOTE: The debug info is inserted as a JUMP_TABLE_DEBUG_INFO node + // when lowering the BR_JT SDNode to an indirect branch. + for (auto I = MBB.instr_rbegin(), E = MBB.instr_rend(); I != E; ++I) { + if (I->isJumpTableDebugInfo()) { + unsigned Index = I->getOperand(0).getImm(); +#ifndef NDEBUG + UsedJTs.set(Index); +#endif + Callback(*JTI, *LastMI, Index); + break; + } + } + } + } + } +#ifndef NDEBUG + assert(UsedJTs.all() && + "Some of jump tables were not used in a debug info instruction"); +#endif + } +} + +void CodeViewDebug::discoverJumpTableBranches(const MachineFunction *MF, + bool isThumb) { + forEachJumpTableBranch( + MF, isThumb, + [this](const MachineJumpTableInfo &, const MachineInstr &BranchMI, + int64_t) { requestLabelBeforeInsn(&BranchMI); }); +} + +void CodeViewDebug::collectDebugInfoForJumpTables(const MachineFunction *MF, + bool isThumb) { + forEachJumpTableBranch( + MF, isThumb, + [this, MF](const MachineJumpTableInfo &JTI, const MachineInstr &BranchMI, + int64_t JumpTableIndex) { + // For label-difference jump tables, find the base expression. + // Otherwise the jump table uses an absolute address (so no base + // is required). + const MCSymbol *Base; + uint64_t BaseOffset = 0; + const MCSymbol *Branch = getLabelBeforeInsn(&BranchMI); + JumpTableEntrySize EntrySize; + switch (JTI.getEntryKind()) { + case MachineJumpTableInfo::EK_Custom32: + case MachineJumpTableInfo::EK_GPRel32BlockAddress: + case MachineJumpTableInfo::EK_GPRel64BlockAddress: + llvm_unreachable( + "EK_Custom32, EK_GPRel32BlockAddress, and " + "EK_GPRel64BlockAddress should never be emitted for COFF"); + case MachineJumpTableInfo::EK_BlockAddress: + // Each entry is an absolute address. + EntrySize = JumpTableEntrySize::Pointer; + Base = nullptr; + break; + case MachineJumpTableInfo::EK_Inline: + case MachineJumpTableInfo::EK_LabelDifference32: + // Ask the AsmPrinter. + std::tie(Base, BaseOffset, Branch, EntrySize) = + Asm->getCodeViewJumpTableInfo(JumpTableIndex, &BranchMI, Branch); + break; + } + + CurFn->JumpTables.push_back( + {EntrySize, Base, BaseOffset, Branch, + MF->getJTISymbol(JumpTableIndex, MMI->getContext()), + JTI.getJumpTables()[JumpTableIndex].MBBs.size()}); + }); +} + +void CodeViewDebug::emitDebugInfoForJumpTables(const FunctionInfo &FI) { + for (auto JumpTable : FI.JumpTables) { + MCSymbol *JumpTableEnd = beginSymbolRecord(SymbolKind::S_ARMSWITCHTABLE); + if (JumpTable.Base) { + OS.AddComment("Base offset"); + OS.emitCOFFSecRel32(JumpTable.Base, JumpTable.BaseOffset); + OS.AddComment("Base section index"); + OS.emitCOFFSectionIndex(JumpTable.Base); + } else { + OS.AddComment("Base offset"); + OS.emitInt32(0); + OS.AddComment("Base section index"); + OS.emitInt16(0); + } + OS.AddComment("Switch type"); + OS.emitInt16(static_cast(JumpTable.EntrySize)); + OS.AddComment("Branch offset"); + OS.emitCOFFSecRel32(JumpTable.Branch, /*Offset=*/0); + OS.AddComment("Table offset"); + OS.emitCOFFSecRel32(JumpTable.Table, /*Offset=*/0); + OS.AddComment("Branch section index"); + OS.emitCOFFSectionIndex(JumpTable.Branch); + OS.AddComment("Table section index"); + OS.emitCOFFSectionIndex(JumpTable.Table); + OS.AddComment("Entries count"); + OS.emitInt32(JumpTable.TableSize); + endSymbolRecord(JumpTableEnd); + } +} diff --git a/llvm/lib/CodeGen/MachineCSE.cpp b/llvm/lib/CodeGen/MachineCSE.cpp --- a/llvm/lib/CodeGen/MachineCSE.cpp +++ b/llvm/lib/CodeGen/MachineCSE.cpp @@ -407,7 +407,7 @@ bool MachineCSE::isCSECandidate(MachineInstr *MI) { if (MI->isPosition() || MI->isPHI() || MI->isImplicitDef() || MI->isKill() || - MI->isInlineAsm() || MI->isDebugInstr()) + MI->isInlineAsm() || MI->isDebugInstr() || MI->isJumpTableDebugInfo()) return false; // Ignore copies. diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp --- a/llvm/lib/CodeGen/MachineInstr.cpp +++ b/llvm/lib/CodeGen/MachineInstr.cpp @@ -1263,7 +1263,8 @@ } if (isPosition() || isDebugInstr() || isTerminator() || - mayRaiseFPException() || hasUnmodeledSideEffects()) + mayRaiseFPException() || hasUnmodeledSideEffects() || + isJumpTableDebugInfo()) return false; // See if this instruction does a load. If so, we have to guarantee that the diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -3905,6 +3905,7 @@ SDValue Chain = Node->getOperand(0); SDValue Table = Node->getOperand(1); SDValue Index = Node->getOperand(2); + int JTI = cast(Table.getNode())->getIndex(); const DataLayout &TD = DAG.getDataLayout(); EVT PTy = TLI.getPointerTy(TD); @@ -3939,7 +3940,7 @@ TLI.getPICJumpTableRelocBase(Table, DAG)); } - Tmp1 = TLI.expandIndirectJTBranch(dl, LD.getValue(1), Addr, DAG); + Tmp1 = TLI.expandIndirectJTBranch(dl, LD.getValue(1), Addr, JTI, DAG); Results.push_back(Tmp1); break; } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -1846,6 +1846,13 @@ return SDValue(N, 0); } +SDValue SelectionDAG::getJumpTableDebugInfo(int JTI, SDValue Chain, + const SDLoc &DL) { + EVT PTy = getTargetLoweringInfo().getPointerTy(getDataLayout()); + return getNode(ISD::JUMP_TABLE_DEBUG_INFO, DL, MVT::Glue, Chain, + getTargetConstant(static_cast(JTI), DL, PTy, true)); +} + SDValue SelectionDAG::getConstantPool(const Constant *C, EVT VT, MaybeAlign Alignment, int Offset, bool isTarget, unsigned TargetFlags) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -125,6 +125,8 @@ case ISD::GlobalTLSAddress: return "GlobalTLSAddress"; case ISD::FrameIndex: return "FrameIndex"; case ISD::JumpTable: return "JumpTable"; + case ISD::JUMP_TABLE_DEBUG_INFO: + return "JUMP_TABLE_DEBUG_INFO"; case ISD::GLOBAL_OFFSET_TABLE: return "GLOBAL_OFFSET_TABLE"; case ISD::RETURNADDR: return "RETURNADDR"; case ISD::ADDROFRETURNADDR: return "ADDROFRETURNADDR"; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -2437,6 +2437,13 @@ return Val; } +void SelectionDAGISel::Select_JUMP_TABLE_DEBUG_INFO(SDNode *N) { + SDLoc dl(N); + CurDAG->SelectNodeTo(N, TargetOpcode::JUMP_TABLE_DEBUG_INFO, MVT::Glue, + CurDAG->getTargetConstant(N->getConstantOperandVal(1), + dl, MVT::i64, true)); +} + /// When a match is complete, this method updates uses of interior chain results /// to use the new results. void SelectionDAGISel::UpdateChains( @@ -2989,6 +2996,9 @@ case ISD::PATCHPOINT: Select_PATCHPOINT(NodeToMatch); return; + case ISD::JUMP_TABLE_DEBUG_INFO: + Select_JUMP_TABLE_DEBUG_INFO(NodeToMatch); + return; } assert(!NodeToMatch->isMachineOpcode() && "Node already selected!"); diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -18,6 +18,7 @@ #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/CodeGen/TargetRegisterInfo.h" @@ -472,6 +473,17 @@ return MCSymbolRefExpr::create(MF->getJTISymbol(JTI, Ctx), Ctx); } +SDValue TargetLowering::expandIndirectJTBranch(const SDLoc &dl, SDValue Value, + SDValue Addr, int JTI, + SelectionDAG &DAG) const { + SDValue Chain = Value; + // Jump table debug info is only needed if CodeView is enabled. + if (DAG.getTarget().getTargetTriple().isOSBinFormatCOFF()) { + Chain = DAG.getJumpTableDebugInfo(JTI, Chain, dl); + } + return DAG.getNode(ISD::BRIND, dl, MVT::Other, Chain, Addr); +} + bool TargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const { const TargetMachine &TM = getTargetMachine(); diff --git a/llvm/lib/DebugInfo/CodeView/EnumTables.cpp b/llvm/lib/DebugInfo/CodeView/EnumTables.cpp --- a/llvm/lib/DebugInfo/CodeView/EnumTables.cpp +++ b/llvm/lib/DebugInfo/CodeView/EnumTables.cpp @@ -434,6 +434,20 @@ CV_ENUM_CLASS_ENT(LabelType, Far), }; +static const EnumEntry JumpTableEntrySizeNames[] = { + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Int8), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, UInt8), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Int16), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, UInt16), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Int32), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, UInt32), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Pointer), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, UInt8ShiftLeft), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, UInt16ShiftLeft), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Int8ShiftLeft), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Int16ShiftLeft), +}; + namespace llvm { namespace codeview { @@ -559,5 +573,9 @@ return ArrayRef(LabelTypeEnum); } +ArrayRef> getJumpTableEntrySizeNames() { + return ArrayRef(JumpTableEntrySizeNames); +} + } // end namespace codeview } // end namespace llvm diff --git a/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp b/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp --- a/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp +++ b/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp @@ -643,6 +643,20 @@ return Error::success(); } +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + JumpTableSym &JumpTable) { + W.printHex("BaseOffset", JumpTable.BaseOffset); + W.printNumber("BaseSegment", JumpTable.BaseSegment); + W.printEnum("SwitchType", static_cast(JumpTable.SwitchType), + getJumpTableEntrySizeNames()); + W.printHex("BranchOffset", JumpTable.BranchOffset); + W.printHex("TableOffset", JumpTable.TableOffset); + W.printNumber("BranchSegment", JumpTable.BranchSegment); + W.printNumber("TableSegment", JumpTable.TableSegment); + W.printNumber("EntriesCount", JumpTable.EntriesCount); + return Error::success(); +} + Error CVSymbolDumperImpl::visitUnknownSymbol(CVSymbol &CVR) { W.printNumber("Length", CVR.length()); return Error::success(); diff --git a/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp b/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp --- a/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp +++ b/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp @@ -483,6 +483,19 @@ return Error::success(); } +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + JumpTableSym &JumpTable) { + error(IO.mapInteger(JumpTable.BaseOffset)); + error(IO.mapInteger(JumpTable.BaseSegment)); + error(IO.mapEnum(JumpTable.SwitchType)); + error(IO.mapInteger(JumpTable.BranchOffset)); + error(IO.mapInteger(JumpTable.TableOffset)); + error(IO.mapInteger(JumpTable.BranchSegment)); + error(IO.mapInteger(JumpTable.TableSegment)); + error(IO.mapInteger(JumpTable.EntriesCount)); + return Error::success(); +} + RegisterId codeview::decodeFramePtrReg(EncodedFramePtrReg EncodedReg, CPUType CPU) { assert(unsigned(EncodedReg) < 4); diff --git a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp --- a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp +++ b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -442,6 +442,7 @@ case SymbolKind::S_THUNK32: case SymbolKind::S_FRAMECOOKIE: case SymbolKind::S_UNAMESPACE: + case SymbolKind::S_ARMSWITCHTABLE: break; // Scope ending symbols. case SymbolKind::S_END: diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp --- a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp @@ -1688,6 +1688,23 @@ return Error::success(); } +// S_ARMSWITCHTABLE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &CVR, + JumpTableSym &JumpTable) { + LLVM_DEBUG({ + W.printHex("BaseOffset", JumpTable.BaseOffset); + W.printNumber("BaseSegment", JumpTable.BaseSegment); + W.printFlags("SwitchType", static_cast(JumpTable.SwitchType), + getJumpTableEntrySizeNames()); + W.printHex("BranchOffset", JumpTable.BranchOffset); + W.printHex("TableOffset", JumpTable.TableOffset); + W.printNumber("BranchSegment", JumpTable.BranchSegment); + W.printNumber("TableSegment", JumpTable.TableSegment); + W.printNumber("EntriesCount", JumpTable.EntriesCount); + }); + return Error::success(); +} + #undef DEBUG_TYPE #define DEBUG_TYPE "CodeViewLogicalVisitor" diff --git a/llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp b/llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp --- a/llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp +++ b/llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp @@ -61,6 +61,7 @@ LLVM_YAML_DECLARE_ENUM_TRAITS(RegisterId) LLVM_YAML_DECLARE_ENUM_TRAITS(TrampolineType) LLVM_YAML_DECLARE_ENUM_TRAITS(ThunkOrdinal) +LLVM_YAML_DECLARE_ENUM_TRAITS(JumpTableEntrySize) LLVM_YAML_STRONG_TYPEDEF(StringRef, TypeName) @@ -207,6 +208,15 @@ } } +void ScalarEnumerationTraits::enumeration( + IO &io, JumpTableEntrySize &FC) { + auto ThunkNames = getJumpTableEntrySizeNames(); + for (const auto &E : ThunkNames) { + io.enumCase(FC, E.Name.str().c_str(), + static_cast(E.Value)); + } +} + namespace llvm { namespace yaml { template <> struct MappingTraits { @@ -586,6 +596,17 @@ IO.mapRequired("Strings", Symbol.Strings); } +template <> void SymbolRecordImpl::map(IO &IO) { + IO.mapRequired("BaseOffset", Symbol.BaseOffset); + IO.mapRequired("BaseSegment", Symbol.BaseSegment); + IO.mapRequired("SwitchType", Symbol.SwitchType); + IO.mapRequired("BranchOffset", Symbol.BranchOffset); + IO.mapRequired("TableOffset", Symbol.TableOffset); + IO.mapRequired("BranchSegment", Symbol.BranchSegment); + IO.mapRequired("TableSegment", Symbol.TableSegment); + IO.mapRequired("EntriesCount", Symbol.EntriesCount); +} + } // end namespace detail } // end namespace CodeViewYAML } // end namespace llvm diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -89,6 +89,10 @@ void emitStartOfAsmFile(Module &M) override; void emitJumpTableInfo() override; + std::tuple + getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr, + const MCSymbol *BranchLabel) const override; void emitFunctionEntryLabel() override; @@ -1062,6 +1066,30 @@ } } +std::tuple +AArch64AsmPrinter::getCodeViewJumpTableInfo(int JTI, + const MachineInstr *BranchInstr, + const MCSymbol *BranchLabel) const { + const auto AFI = MF->getInfo(); + const auto Base = AArch64FI->getJumpTableEntryPCRelSymbol(JTI); + codeview::JumpTableEntrySize EntrySize; + switch (AFI->getJumpTableEntrySize(JTI)) { + case 1: + EntrySize = codeview::JumpTableEntrySize::UInt8ShiftLeft; + break; + case 2: + EntrySize = codeview::JumpTableEntrySize::UInt16ShiftLeft; + break; + case 4: + EntrySize = codeview::JumpTableEntrySize::Int32; + break; + default: + llvm_unreachable("Unexpected jump table entry size"); + } + return std::make_tuple(Base, 0, BranchLabel, EntrySize); +} + void AArch64AsmPrinter::emitFunctionEntryLabel() { if (MF->getFunction().getCallingConv() == CallingConv::AArch64_VectorCall || MF->getFunction().getCallingConv() == diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -9422,8 +9422,8 @@ SDNode *Dest = DAG.getMachineNode(AArch64::JumpTableDest32, DL, MVT::i64, MVT::i64, JT, Entry, DAG.getTargetJumpTable(JTI, MVT::i32)); - return DAG.getNode(ISD::BRIND, DL, MVT::Other, Op.getOperand(0), - SDValue(Dest, 0)); + SDValue JTInfo = DAG.getJumpTableDebugInfo(JTI, Op.getOperand(0), DL); + return DAG.getNode(ISD::BRIND, DL, MVT::Other, JTInfo, SDValue(Dest, 0)); } SDValue AArch64TargetLowering::LowerConstantPool(SDValue Op, diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp --- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -3632,6 +3632,9 @@ auto JumpTableInst = MIB.buildInstr(AArch64::JumpTableDest32, {TargetReg, ScratchReg}, {JTAddr, Index}) .addJumpTableIndex(JTI); + // Save the jump table info. + MIB.buildInstr(TargetOpcode::JUMP_TABLE_DEBUG_INFO, {}, + {static_cast(JTI)}); // Build the indirect branch. MIB.buildInstr(AArch64::BR, {}, {TargetReg}); I.eraseFromParent(); diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.h b/llvm/lib/Target/ARM/ARMAsmPrinter.h --- a/llvm/lib/Target/ARM/ARMAsmPrinter.h +++ b/llvm/lib/Target/ARM/ARMAsmPrinter.h @@ -89,6 +89,10 @@ void emitJumpTableTBInst(const MachineInstr *MI, unsigned OffsetWidth); void emitInstruction(const MachineInstr *MI) override; bool runOnMachineFunction(MachineFunction &F) override; + std::tuple + getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr, + const MCSymbol *BranchLabel) const override; void emitConstantPool() override { // we emit constant pools customly! diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp --- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp +++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp @@ -1117,6 +1117,50 @@ emitAlignment(Align(2)); } +std::tuple +ARMAsmPrinter::getCodeViewJumpTableInfo(int JTI, + const MachineInstr *BranchInstr, + const MCSymbol *BranchLabel) const { + codeview::JumpTableEntrySize EntrySize; + const MCSymbol *BaseLabel; + uint64_t BaseOffset = 0; + switch (BranchInstr->getOpcode()) { + case ARM::BR_JTadd: + case ARM::BR_JTr: + case ARM::tBR_JTr: + // Word relative to the jump table address. + EntrySize = codeview::JumpTableEntrySize::UInt32; + BaseLabel = GetARMJTIPICJumpTableLabel(JTI); + break; + case ARM::tTBH_JT: + case ARM::t2TBH_JT: + // half-word shifted left, relative to *after* the branch instruction. + EntrySize = codeview::JumpTableEntrySize::UInt16ShiftLeft; + BranchLabel = GetCPISymbol(BranchInstr->getOperand(3).getImm()); + BaseLabel = BranchLabel; + BaseOffset = 4; + break; + case ARM::tTBB_JT: + case ARM::t2TBB_JT: + // byte shifted left, relative to *after* the branch instruction. + EntrySize = codeview::JumpTableEntrySize::UInt8ShiftLeft; + BranchLabel = GetCPISymbol(BranchInstr->getOperand(3).getImm()); + BaseLabel = BranchLabel; + BaseOffset = 4; + break; + case ARM::t2BR_JT: + // Direct jump. + BaseLabel = nullptr; + EntrySize = codeview::JumpTableEntrySize::Pointer; + break; + default: + llvm_unreachable("Unknown jump table instruction"); + } + + return std::make_tuple(BaseLabel, BaseOffset, BranchLabel, EntrySize); +} + void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) { assert(MI->getFlag(MachineInstr::FrameSetup) && "Only instruction which are involved into frame setup code are allowed"); diff --git a/llvm/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h --- a/llvm/lib/Target/X86/X86ISelLowering.h +++ b/llvm/lib/Target/X86/X86ISelLowering.h @@ -1562,9 +1562,8 @@ bool lowerInterleavedStore(StoreInst *SI, ShuffleVectorInst *SVI, unsigned Factor) const override; - SDValue expandIndirectJTBranch(const SDLoc& dl, SDValue Value, - SDValue Addr, SelectionDAG &DAG) - const override; + SDValue expandIndirectJTBranch(const SDLoc &dl, SDValue Value, SDValue Addr, + int JTI, SelectionDAG &DAG) const override; Align getPrefLoopAlignment(MachineLoop *ML) const override; diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -56007,8 +56007,9 @@ return true; } -SDValue X86TargetLowering::expandIndirectJTBranch(const SDLoc& dl, +SDValue X86TargetLowering::expandIndirectJTBranch(const SDLoc &dl, SDValue Value, SDValue Addr, + int JTI, SelectionDAG &DAG) const { const Module *M = DAG.getMachineFunction().getMMI().getModule(); Metadata *IsCFProtectionSupported = M->getModuleFlag("cf-protection-branch"); @@ -56017,10 +56018,11 @@ // notrack prefix to the indirect branch. // In order to do that we create NT_BRIND SDNode. // Upon ISEL, the pattern will convert it to jmp with NoTrack prefix. - return DAG.getNode(X86ISD::NT_BRIND, dl, MVT::Other, Value, Addr); + SDValue JTInfo = DAG.getJumpTableDebugInfo(JTI, Value, dl); + return DAG.getNode(X86ISD::NT_BRIND, dl, MVT::Other, JTInfo, Addr); } - return TargetLowering::expandIndirectJTBranch(dl, Value, Addr, DAG); + return TargetLowering::expandIndirectJTBranch(dl, Value, Addr, JTI, DAG); } TargetLowering::AndOrSETCCFoldKind diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt-constrain.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt-constrain.mir --- a/llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt-constrain.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt-constrain.mir @@ -35,6 +35,7 @@ ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: [[MOVaddrJT:%[0-9]+]]:gpr64common = MOVaddrJT target-flags(aarch64-page) %jump-table.0, target-flags(aarch64-pageoff, aarch64-nc) %jump-table.0 ; CHECK-NEXT: early-clobber %5:gpr64, early-clobber %6:gpr64sp = JumpTableDest32 [[MOVaddrJT]], [[SUBREG_TO_REG]], %jump-table.0 + ; CHECK-NEXT: JUMP_TABLE_DEBUG_INFO 0 ; CHECK-NEXT: BR %5 ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.2: diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt.mir --- a/llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt.mir @@ -71,6 +71,7 @@ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32 = COPY $wzr ; CHECK-NEXT: [[MOVaddrJT:%[0-9]+]]:gpr64common = MOVaddrJT target-flags(aarch64-page) %jump-table.0, target-flags(aarch64-pageoff, aarch64-nc) %jump-table.0 ; CHECK-NEXT: early-clobber %17:gpr64, early-clobber %18:gpr64sp = JumpTableDest32 [[MOVaddrJT]], [[SUBREG_TO_REG]], %jump-table.0 + ; CHECK-NEXT: JUMP_TABLE_DEBUG_INFO 0 ; CHECK-NEXT: BR %17 ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.2.sw.bb: diff --git a/llvm/test/DebugInfo/COFF/jump-table-with-indirect-ptr-null.ll b/llvm/test/DebugInfo/COFF/jump-table-with-indirect-ptr-null.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/COFF/jump-table-with-indirect-ptr-null.ll @@ -0,0 +1,73 @@ +; REQUIRES: x86-registered-target +; RUN: llc < %s | FileCheck %s + +; Repro for issue https://reviews.llvm.org/D149367#4619121 +; Validates that `indirect ptr null` and a jump table can be used in the same function. + +; Verify branch labels match what's in the CodeView +; CHECK: .Ltmp2: +; CHECK-NEXT: jmpq *%{{.*}} + +; Verify jump table have the same entry size, base offset and shift as what's in the CodeView +; CHECK: {{\.?}}LJTI0_0: +; CHECK-NEXT: .long .LBB0_[[#]]-.LJTI0_0 + +; Verify CodeView +; CHECK: .short 4441 # Record kind: S_ARMSWITCHTABLE +; CHECK-NEXT: .secrel32 .LJTI0_0 # Base offset +; CHECK-NEXT: .secidx .LJTI0_0 # Base section index +; CHECK-NEXT: .short 4 # Switch type +; CHECK-NEXT: .secrel32 .Ltmp2 # Branch offset +; CHECK-NEXT: .secrel32 .LJTI0_0 # Table offset +; CHECK-NEXT: .secidx .Ltmp2 # Branch section index +; CHECK-NEXT: .secidx .LJTI0_0 # Table section index +; CHECK-NEXT: .long 4 # Entries count +; CHECK-NOT: .short 4441 # Record kind: S_ARMSWITCHTABLE + +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.34.0" + +define i32 @f() !dbg !5 { +entry: + indirectbr ptr null, [label %BC_SUCCEED], !dbg !11 + +BC_SUCCEED: ; preds = %entry + %0 = lshr i64 0, 0 + switch i64 %0, label %sw.default.i.i2445 [ + i64 3, label %sw.bb15.i.i + i64 1, label %sw.bb7.i.i + i64 2, label %sw.bb11.i.i2444 + i64 0, label %sw.bb3.i.i + ] + +sw.bb3.i.i: ; preds = %BC_SUCCEED + ret i32 0 + +sw.bb7.i.i: ; preds = %BC_SUCCEED + ret i32 0 + +sw.bb11.i.i2444: ; preds = %BC_SUCCEED + ret i32 0 + +sw.bb15.i.i: ; preds = %BC_SUCCEED + ret i32 0 + +sw.default.i.i2445: ; preds = %BC_SUCCEED + ret i32 0 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2, globals: !2, imports: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "../../v8/src/regexp\\regexp-interpreter.cc", directory: ".", checksumkind: CSK_MD5, checksum: "ddba353f72137fb1d64b5fc8ee071a9c") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = distinct !DISubprogram(name: "f", linkageName: "f", scope: !7, file: !6, line: 386, type: !10, scopeLine: 391, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, templateParams: !2, retainedNodes: !2) +!6 = !DIFile(filename: "../../v8/src/regexp/regexp-interpreter.cc", directory: ".", checksumkind: CSK_MD5, checksum: "ddba353f72137fb1d64b5fc8ee071a9c") +!7 = !DINamespace(scope: !8) +!8 = !DINamespace(name: "internal", scope: !9) +!9 = !DINamespace(name: "v8", scope: null) +!10 = distinct !DISubroutineType(types: !2) +!11 = !DILocation(line: 1, scope: !5) \ No newline at end of file diff --git a/llvm/test/DebugInfo/COFF/jump-table.ll b/llvm/test/DebugInfo/COFF/jump-table.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/COFF/jump-table.ll @@ -0,0 +1,262 @@ +; REQUIRES: arm-registered-target +; REQUIRES: aarch64-registered-target +; REQUIRES: x86-registered-target +; RUN: llc -mtriple=i686-windows < %s | FileCheck %s --check-prefixes=CHECK,I686,NOTA32 +; RUN: llc -mtriple=x86_64-windows < %s | FileCheck %s --check-prefixes=CHECK,X64,NOTA32 +; RUN: llc -mtriple=aarch64-windows < %s | FileCheck %s --check-prefixes=CHECK,A64,NOTA32 +; RUN: llc -mtriple=thumbv7a-windows < %s | FileCheck %s --check-prefixes=CHECK,A32 +; RUN: llc -mtriple=x86_64-windows -filetype=obj < %s | llvm-readobj - --codeview | FileCheck %s --check-prefixes=CV + +; Generated by clang++ -S -c -std=c++11 -emit-llvm -g from the following C++11 source: +; extern "C" void f1(); +; extern "C" void f2(); +; extern "C" void f3(); +; extern "C" void f4(); +; extern "C" void f5(); +; extern "C" void func(int i){ +; switch (i) { +; case 0: f1(); break; +; case 1: f2(); break; +; case 2: f3(); break; +; case 3: f4(); break; +; } +; switch (i) { +; case 1: f2(); break; +; case 2: f3(); break; +; case 3: f4(); break; +; case 4: f5(); break; +; case 5: f1(); break; +; } +; } + +; i686 entries are absolute addresses (Base = 0, SwitchType = Pointer). +; x86_64 entries are fixed-size and relative to the jump table (Base = Table, +; SwitchType = Int32). +; aarch64 entries are variable-sized and relative to the first entry's BB if +; compressed (Base = Branch+0x4, SwitchType = UInt8ShiftLeft/UInt16ShiftLeft) +; otherwise relative to the ADR instruction (Base = Branch-0xc, SwitchType = +; Int32). +; thumbv7a entries are either absolute addresses (Base = 0, SwitchType = +; Pointer) OR variable-sized and relative to *after* the branch instruction +; (Base = Branch+0x4, SwitchType = UInt8ShiftLeft/UInt16ShiftLeft/UInt32) but +; there appears to be a bug where the offsets are always 0. + +; Verify branch labels match what's in the CodeView +; X64: .Ltmp1: +; X64-NEXT: jmpq *%{{.*}} +; X64: .Ltmp4: +; X64-NEXT: jmpq *%{{.*}} +; A32: .LCPI0_0: +; A32-NEXT add pc, r{{.*}} +; NOTE: thumbv7a places the jump tables just after the branch, so verify the other branch below +; A64: .Ltmp1: +; A64-NEXT: br x{{.*}} +; A64: .Ltmp4: +; A64-NEXT: br x{{.*}} + +; Verify jump table have the same entry size, base offset and shift as what's in the CodeView +; CHECK: {{\.?}}LJTI0_0: +; I686-NEXT: .long LBB0_[[#]] +; X64-NEXT: .long .LBB0_[[#]]-.LJTI0_0 +; A32-NEXT: .byte (($MBB0_[[#]])-(.LCPI0_0+4))/2 +; A64-NEXT: .byte (.LBB0_[[FIRSTBLOCK:[0-9]+]]-.LBB0_[[FIRSTBLOCK]])>>2 +; NOTE: thumbv7a places the jump tables just after the branch, so check for the other branch now +; A32: .LCPI0_1: +; A32-NEXT add pc, r{{.*}} +; CHECK: {{\.?}}LJTI0_1: +; I686-NEXT: .long LBB0_[[#]] +; X64-NEXT: .long .LBB0_[[#]]-.LJTI0_1 +; A32-NEXT: .byte (($MBB0_[[#]])-(.LCPI0_1+4))/2 +; A64-NEXT: .byte (.LBB0_[[SECONDBLOCK:[0-9]+]]-.LBB0_[[SECONDBLOCK]])>>2 + +; Verify CodeView +; CHECK: [[INT16:\.short|\.hword]] 4441 [[COMMENT:#|//|@]] Record kind: S_ARMSWITCHTABLE +; I686-NEXT: .long 0 [[COMMENT]] Base offset +; I686-NEXT: .short 0 [[COMMENT]] Base section index +; X64-NEXT: .secrel32 .LJTI0_0 [[COMMENT]] Base offset +; X64-NEXT: .secidx .LJTI0_0 [[COMMENT]] Base section index +; A32-NEXT: .secrel32 .LCPI0_0+4 [[COMMENT]] Base offset +; A32-NEXT: .secidx .LCPI0_0 [[COMMENT]] Base section index +; A64-NEXT: .secrel32 .LBB0_[[FIRSTBLOCK]] [[COMMENT]] Base offset +; A64-NEXT: .secidx .LBB0_[[FIRSTBLOCK]] [[COMMENT]] Base section index +; I686-NEXT: .short 6 [[COMMENT]] Switch type +; X64-NEXT: .short 4 [[COMMENT]] Switch type +; A32-NEXT: .short 7 [[COMMENT]] Switch type +; A64-NEXT: .hword 7 [[COMMENT]] Switch type +; NOTA32-NEXT: .secrel32 {{\.?}}Ltmp1 [[COMMENT]] Branch offset +; A32-NEXT: .secrel32 .LCPI0_0 [[COMMENT]] Branch offset +; CHECK-NEXT: .secrel32 {{\.?}}LJTI0_0 [[COMMENT]] Table offset +; NOTA32-NEXT: .secidx {{\.?}}Ltmp1 [[COMMENT]] Branch section index +; A32-NEXT: .secidx .LCPI0_0 [[COMMENT]] Branch section index +; CHECK-NEXT: .secidx {{\.?}}LJTI0_0 [[COMMENT]] Table section index +; CHECK-NEXT: [[INT32:\.long|\.word]] 4 [[COMMENT]] Entries count +; CHECK: [[INT16]] 4441 [[COMMENT]] Record kind: S_ARMSWITCHTABLE +; I686-NEXT: .long 0 [[COMMENT]] Base offset +; I686-NEXT: .short 0 [[COMMENT]] Base section index +; X64-NEXT: .secrel32 .LJTI0_1 [[COMMENT]] Base offset +; X64-NEXT: .secidx .LJTI0_1 [[COMMENT]] Base section index +; A32-NEXT: .secrel32 .LCPI0_1+4 [[COMMENT]] Base offset +; A32-NEXT: .secidx .LCPI0_1 [[COMMENT]] Base section index +; A64-NEXT: .secrel32 .LBB0_[[SECONDBLOCK]] [[COMMENT]] Base offset +; A64-NEXT: .secidx .LBB0_[[SECONDBLOCK]] [[COMMENT]] Base section index +; I686-NEXT: .short 6 [[COMMENT]] Switch type +; X64-NEXT: .short 4 [[COMMENT]] Switch type +; A32-NEXT: .short 7 [[COMMENT]] Switch type +; A64-NEXT: .hword 7 [[COMMENT]] Switch type +; NOTA32-NEXT: .secrel32 {{\.?}}Ltmp4 [[COMMENT]] Branch offset +; A32-NEXT: .secrel32 .LCPI0_1 [[COMMENT]] Branch offset +; CHECK-NEXT: .secrel32 {{\.?}}LJTI0_1 [[COMMENT]] Table offset +; NOTA32-NEXT: .secidx {{\.?}}Ltmp4 [[COMMENT]] Branch section index +; A32-NEXT: .secidx .LCPI0_1 [[COMMENT]] Branch section index +; CHECK-NEXT: .secidx {{\.?}}LJTI0_1 [[COMMENT]] Table section index +; CHECK-NEXT: [[INT32]] 5 [[COMMENT]] Entries count +; CHECK-NOT: [[INT16]] 4441 [[COMMENT]] Record kind: S_ARMSWITCHTABLE + +; Verify CodeView as dumped by llvm-readobj +; CV: Subsection [ +; CV: SubSectionType: Symbols (0xF1) +; CV: GlobalProcIdSym { +; CV: DisplayName: func +; CV-NOT: GlobalProcIdSym +; CV: JumpTableSym { +; CV-NEXT: Kind: S_ARMSWITCHTABLE (0x1159) +; CV-NEXT: BaseOffset: 0x0 +; CV-NEXT: BaseSegment: 0 +; CV-NEXT: SwitchType: Int32 (0x4) +; CV-NEXT: BranchOffset: 0x23 +; CV-NEXT: TableOffset: 0x0 +; CV-NEXT: BranchSegment: 0 +; CV-NEXT: TableSegment: 0 +; CV-NEXT: EntriesCount: 4 +; CV-NEXT: } +; CV-NEXT: JumpTableSym { +; CV-NEXT: Kind: S_ARMSWITCHTABLE (0x1159) +; CV-NEXT: BaseOffset: 0x10 +; CV-NEXT: BaseSegment: 0 +; CV-NEXT: SwitchType: Int32 (0x4) +; CV-NEXT: BranchOffset: 0x5A +; CV-NEXT: TableOffset: 0x10 +; CV-NEXT: BranchSegment: 0 +; CV-NEXT: TableSegment: 0 +; CV-NEXT: EntriesCount: 5 +; CV-NEXT: } +; CV-NOT: JumpTableSym { + +source_filename = ".\\jump-table.cpp" +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.35.32216" + +; Function Attrs: mustprogress noinline optnone uwtable +define dso_local void @func(i32 noundef %0) #0 !dbg !8 { + %2 = alloca i32, align 4 + store i32 %0, ptr %2, align 4 + call void @llvm.dbg.declare(metadata ptr %2, metadata !14, metadata !DIExpression()), !dbg !15 + %3 = load i32, ptr %2, align 4, !dbg !16 + switch i32 %3, label %8 [ + i32 0, label %4 + i32 1, label %5 + i32 2, label %6 + i32 3, label %7 + ], !dbg !16 + +4: ; preds = %1 + call void @f1(), !dbg !17 + br label %8, !dbg !17 + +5: ; preds = %1 + call void @f2(), !dbg !19 + br label %8, !dbg !19 + +6: ; preds = %1 + call void @f3(), !dbg !20 + br label %8, !dbg !20 + +7: ; preds = %1 + call void @f4(), !dbg !21 + br label %8, !dbg !21 + +8: ; preds = %1, %7, %6, %5, %4 + %9 = load i32, ptr %2, align 4, !dbg !22 + switch i32 %9, label %15 [ + i32 1, label %10 + i32 2, label %11 + i32 3, label %12 + i32 4, label %13 + i32 5, label %14 + ], !dbg !22 + +10: ; preds = %8 + call void @f2(), !dbg !23 + br label %15, !dbg !23 + +11: ; preds = %8 + call void @f3(), !dbg !25 + br label %15, !dbg !25 + +12: ; preds = %8 + call void @f4(), !dbg !26 + br label %15, !dbg !26 + +13: ; preds = %8 + call void @f5(), !dbg !27 + br label %15, !dbg !27 + +14: ; preds = %8 + call void @f1(), !dbg !28 + br label %15, !dbg !28 + +15: ; preds = %8, %14, %13, %12, %11, %10 + ret void, !dbg !29 +} + +; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +declare dso_local void @f1() #2 + +declare dso_local void @f2() #2 + +declare dso_local void @f3() #2 + +declare dso_local void @f4() #2 + +declare dso_local void @f5() #2 + +attributes #0 = { mustprogress noinline optnone uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nocallback nofree nosync nounwind readnone speculatable willreturn } +attributes #2 = { "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_11, file: !1, producer: "clang version 15.0.1", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "jump-table.cpp", directory: "C:\\llvm", checksumkind: CSK_MD5, checksum: "35610c7104c8080f83e2bf6a02dabfc9") +!2 = !{i32 2, !"CodeView", i32 1} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 2} +!5 = !{i32 7, !"PIC Level", i32 2} +!6 = !{i32 7, !"uwtable", i32 2} +!7 = !{!"clang version 15.0.1"} +!8 = distinct !DISubprogram(name: "func", scope: !9, file: !9, line: 6, type: !10, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !13) +!9 = !DIFile(filename: ".\\jump-table.cpp", directory: "C:\\llvm", checksumkind: CSK_MD5, checksum: "35610c7104c8080f83e2bf6a02dabfc9") +!10 = !DISubroutineType(types: !11) +!11 = !{null, !12} +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !{} +!14 = !DILocalVariable(name: "i", arg: 1, scope: !8, file: !9, line: 6, type: !12) +!15 = !DILocation(line: 6, scope: !8) +!16 = !DILocation(line: 7, scope: !8) +!17 = !DILocation(line: 8, scope: !18) +!18 = distinct !DILexicalBlock(scope: !8, file: !9, line: 7) +!19 = !DILocation(line: 9, scope: !18) +!20 = !DILocation(line: 10, scope: !18) +!21 = !DILocation(line: 11, scope: !18) +!22 = !DILocation(line: 13, scope: !8) +!23 = !DILocation(line: 14, scope: !24) +!24 = distinct !DILexicalBlock(scope: !8, file: !9, line: 13) +!25 = !DILocation(line: 15, scope: !24) +!26 = !DILocation(line: 16, scope: !24) +!27 = !DILocation(line: 17, scope: !24) +!28 = !DILocation(line: 18, scope: !24) +!29 = !DILocation(line: 20, scope: !8) diff --git a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp --- a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -358,6 +358,23 @@ return typesetItemList(GapStrs, 7, IndentLevel, ", "); } +static std::string formatJumpTableEntrySize(JumpTableEntrySize EntrySize) { + switch (EntrySize) { + RETURN_CASE(JumpTableEntrySize, Int8, "int8"); + RETURN_CASE(JumpTableEntrySize, UInt8, "uin8"); + RETURN_CASE(JumpTableEntrySize, Int16, "int16"); + RETURN_CASE(JumpTableEntrySize, UInt16, "uint16"); + RETURN_CASE(JumpTableEntrySize, Int32, "int32"); + RETURN_CASE(JumpTableEntrySize, UInt32, "uint32"); + RETURN_CASE(JumpTableEntrySize, Pointer, "pointer"); + RETURN_CASE(JumpTableEntrySize, UInt8ShiftLeft, "uint8shl"); + RETURN_CASE(JumpTableEntrySize, UInt16ShiftLeft, "uint16shl"); + RETURN_CASE(JumpTableEntrySize, Int8ShiftLeft, "int8shl"); + RETURN_CASE(JumpTableEntrySize, Int16ShiftLeft, "int16shl"); + } + return formatUnknownEnum(EntrySize); +} + Error MinimalSymbolDumper::visitSymbolBegin(codeview::CVSymbol &Record) { return visitSymbolBegin(Record, 0); } @@ -905,3 +922,17 @@ Annot.Strings)); return Error::success(); } + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + JumpTableSym &JumpTable) { + AutoIndent Indent(P, 7); + P.formatLine( + "base = {0}, switchtype = {1}, branch = {2}, table = {3}, entriescount = " + "{4}", + formatSegmentOffset(JumpTable.BaseSegment, JumpTable.BaseOffset), + formatJumpTableEntrySize(JumpTable.SwitchType), + formatSegmentOffset(JumpTable.BranchSegment, JumpTable.BranchOffset), + formatSegmentOffset(JumpTable.TableSegment, JumpTable.TableOffset), + JumpTable.EntriesCount); + return Error::success(); +}