Index: llvm/include/llvm/CodeGen/AsmPrinter.h =================================================================== --- llvm/include/llvm/CodeGen/AsmPrinter.h +++ 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 //===------------------------------------------------------------------===// Index: llvm/include/llvm/DebugInfo/CodeView/CodeView.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/CodeView.h +++ 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, +}; } } Index: llvm/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def +++ 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) Index: llvm/include/llvm/DebugInfo/CodeView/EnumTables.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/EnumTables.h +++ 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 Index: llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h +++ 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) {} Index: llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h +++ 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. Index: llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -4153,3 +4153,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); +} Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -22,6 +22,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" @@ -132,6 +133,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 { @@ -159,6 +169,8 @@ std::vector> HeapAllocSites; + std::vector JumpTables; + const MCSymbol *Begin = nullptr; const MCSymbol *End = nullptr; unsigned FuncId = 0; @@ -475,6 +487,10 @@ unsigned getPointerSizeInBytes(); + void discoverPotentialJumpTableBranches(const MachineFunction *MF); + void collectDebugInfoForJumpTables(const MachineFunction *MF); + void emitDebugInfoForJumpTables(const FunctionInfo &FI); + protected: /// Gather pre-function debug information. void beginFunctionImpl(const MachineFunction *MF) override; Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -26,6 +26,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" @@ -1236,6 +1237,8 @@ if (SP != nullptr) emitDebugInfoForUDTs(LocalUDTs); + emitDebugInfoForJumpTables(FI); + // We're done with this function. emitEndSymbolRecord(SymbolKind::S_PROC_ID_END); } @@ -1568,6 +1571,9 @@ } } } + + // Mark branches that may potentially be using jump tables with labels. + discoverPotentialJumpTableBranches(MF); } static bool shouldEmitUdt(const DIType *T) { @@ -3073,6 +3079,8 @@ } } + collectDebugInfoForJumpTables(MF); + CurFn->Annotations = MF->getCodeViewAnnotations(); CurFn->End = Asm->getFunctionEnd(); @@ -3432,3 +3440,119 @@ emitConstantSymbolRecord(DIGV->getType(), Value, QualifiedName); } } + +void CodeViewDebug::discoverPotentialJumpTableBranches( + const MachineFunction *MF) { + auto JTI = MF->getJumpTableInfo(); + if (JTI && !JTI->isEmpty()) { + for (const auto &MBB : *MF) { + if (MBB.empty()) { + // Skip empty blocks. + continue; + } + + const auto LastMI = std::prev(MBB.instr_end()); + // Label all indirect branches at the end of BBs, in case they are using + // a jump table. + if (LastMI->isIndirectBranch()) { + requestLabelBeforeInsn(&*LastMI); + } + } + } +} + +void CodeViewDebug::collectDebugInfoForJumpTables(const MachineFunction *MF) { + const auto JTI = MF->getJumpTableInfo(); + + if (JTI && !JTI->isEmpty()) { + Register JTIReg; + for (const auto &MBB : *MF) { + if (MBB.empty()) { + // Skip empty blocks. + continue; + } + + // We only care about BBs that end with an indirect branch. + const auto LastMI = std::prev(MBB.instr_end()); + if (LastMI->isIndirectBranch()) { + bool foundJTIUse = false; + for (const auto &MI : MBB) { + for (const auto &MO : MI.operands()) { + // If the BB includes the use of a jump table then record the branch + // and the jump table that it uses in the debug info. + if (MO.getType() == MachineOperand::MO_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(&*LastMI); + 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(MO.getIndex(), &*LastMI, + Branch); + break; + default: + llvm_unreachable("Unknown JumpTableEntryKind"); + } + + CurFn->JumpTables.push_back( + {EntrySize, Base, BaseOffset, Branch, + MF->getJTISymbol(MO.getIndex(), MMI->getContext()), + JTI->getJumpTables()[MO.getIndex()].MBBs.size()}); + foundJTIUse = true; + break; + } + } + if (foundJTIUse) + break; + } + } + } + } +} + +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); + } +} Index: llvm/lib/DebugInfo/CodeView/EnumTables.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/EnumTables.cpp +++ 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 Index: llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp +++ 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(); Index: llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp +++ 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); Index: llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp +++ 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: Index: llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp +++ 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" Index: llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp =================================================================== --- llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp +++ 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 Index: llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ 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; @@ -967,6 +971,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::UInt32; + 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() == Index: llvm/lib/Target/ARM/ARMAsmPrinter.h =================================================================== --- llvm/lib/Target/ARM/ARMAsmPrinter.h +++ 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! Index: llvm/lib/Target/ARM/ARMAsmPrinter.cpp =================================================================== --- llvm/lib/Target/ARM/ARMAsmPrinter.cpp +++ llvm/lib/Target/ARM/ARMAsmPrinter.cpp @@ -1100,6 +1100,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"); Index: llvm/test/DebugInfo/COFF/jump-table.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/COFF/jump-table.ll @@ -0,0 +1,205 @@ +; RUN: llc -mtriple=i686-windows -filetype=obj < %s | llvm-readobj - --codeview | FileCheck %s --check-prefixes=CHECK,I686 +; RUN: llc -mtriple=x86_64-windows -filetype=obj < %s | llvm-readobj - --codeview | FileCheck %s --check-prefixes=CHECK,X64 +; RUN: llc -mtriple=aarch64-windows -filetype=obj < %s | llvm-readobj - --codeview | FileCheck %s --check-prefixes=CHECK,A64 +; RUN: llc -mtriple=thumbv7a-windows -filetype=obj < %s | llvm-readobj - --codeview | FileCheck %s --check-prefixes=CHECK,A32 + +; 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 +; (Base = Branch+0x4, SwitchType = UInt8ShiftLeft/UInt16ShiftLeft/UInt32). +; 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. + +; CHECK: Subsection [ +; CHECK: SubSectionType: Symbols (0xF1) +; CHECK: GlobalProcIdSym { +; CHECK: DisplayName: func +; CHECK-NOT: GlobalProcIdSym +; CHECK: JumpTableSym { +; CHECK-NEXT: Kind: S_ARMSWITCHTABLE (0x1159) +; I686-NEXT: BaseOffset: 0x0 +; X64-NEXT: BaseOffset: 0x0 +; A64-NEXT: BaseOffset: 0x2C +; A32-NEXT: BaseOffset: 0x4 +; CHECK-NEXT: BaseSegment: 0 +; I686-NEXT: SwitchType: Pointer (0x6) +; X64-NEXT: SwitchType: Int32 (0x4) +; A64-NEXT: SwitchType: UInt8ShiftLeft (0x7) +; A32-NEXT: SwitchType: UInt8ShiftLeft (0x7) +; I686-NEXT: BranchOffset: 0x16 +; X64-NEXT: BranchOffset: 0x23 +; A64-NEXT: BranchOffset: 0x28 +; A32-NEXT: BranchOffset: 0x0 +; CHECK-NEXT: TableOffset: 0x0 +; CHECK-NEXT: BranchSegment: 0 +; CHECK-NEXT: TableSegment: 0 +; CHECK-NEXT: EntriesCount: 4 +; CHECK-NEXT: } +; CHECK-NEXT: JumpTableSym { +; CHECK-NEXT: Kind: S_ARMSWITCHTABLE (0x1159) +; I686-NEXT: BaseOffset: 0x0 +; X64-NEXT: BaseOffset: 0x10 +; A64-NEXT: BaseOffset: 0x74 +; A32-NEXT: BaseOffset: 0x4 +; CHECK-NEXT: BaseSegment: 0 +; I686-NEXT: SwitchType: Pointer (0x6) +; X64-NEXT: SwitchType: Int32 (0x4) +; A64-NEXT: SwitchType: UInt8ShiftLeft (0x7) +; A32-NEXT: SwitchType: UInt8ShiftLeft (0x7) +; I686-NEXT: BranchOffset: 0x45 +; X64-NEXT: BranchOffset: 0x5A +; A64-NEXT: BranchOffset: 0x70 +; A32-NEXT: BranchOffset: 0x0 +; I686-NEXT: TableOffset: 0x10 +; X64-NEXT: TableOffset: 0x10 +; A64-NEXT: TableOffset: 0x4 +; A32-NEXT: TableOffset: 0x0 +; CHECK-NEXT: BranchSegment: 0 +; CHECK-NEXT: TableSegment: 0 +; CHECK-NEXT: EntriesCount: 5 +; CHECK-NEXT: } +; CHECK-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) Index: llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp =================================================================== --- llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -357,6 +357,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); } @@ -904,3 +921,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(); +}