[llvm] 0c5c7b5 - Emit the CodeView `S_ARMSWITCHTABLE` debug symbol for jump tables

Daniel Paoliello via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 31 12:15:54 PDT 2023


Author: Daniel Paoliello
Date: 2023-08-31T12:06:50-07:00
New Revision: 0c5c7b52f0f395a6beb7956ba210b8ca727c0471

URL: https://github.com/llvm/llvm-project/commit/0c5c7b52f0f395a6beb7956ba210b8ca727c0471
DIFF: https://github.com/llvm/llvm-project/commit/0c5c7b52f0f395a6beb7956ba210b8ca727c0471.diff

LOG: Emit the CodeView `S_ARMSWITCHTABLE` debug symbol for jump tables

The CodeView `S_ARMSWITCHTABLE` debug symbol is used to describe the layout of a jump table, it contains the following information:

* The address of the branch instruction that uses the jump table.
* The address of the jump table.
* The "base" address that the values in the jump table are relative to.
* The type of each entry (absolute pointer, a relative integer, a relative integer that is shifted).

Together this information can be used by debuggers and binary analysis tools to understand what an jump table indirect branch is doing and where it might jump to.

Documentation for the symbol can be found in the Microsoft PDB library dumper: https://github.com/microsoft/microsoft-pdb/blob/0fe89a942f9a0f8e061213313e438884f4c9b876/cvdump/dumpsym7.cpp#L5518

This change adds support to LLVM to emit the `S_ARMSWITCHTABLE` debug symbol as well as to dump it out (for testing purposes).

Reviewed By: efriedma

Differential Revision: https://reviews.llvm.org/D149367

Added: 
    llvm/test/DebugInfo/COFF/jump-table-with-indirect-ptr-null.ll
    llvm/test/DebugInfo/COFF/jump-table.ll

Modified: 
    llvm/include/llvm/CodeGen/AsmPrinter.h
    llvm/include/llvm/CodeGen/ISDOpcodes.h
    llvm/include/llvm/CodeGen/MachineInstr.h
    llvm/include/llvm/CodeGen/SelectionDAG.h
    llvm/include/llvm/CodeGen/SelectionDAGISel.h
    llvm/include/llvm/CodeGen/TargetLowering.h
    llvm/include/llvm/DebugInfo/CodeView/CodeView.h
    llvm/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def
    llvm/include/llvm/DebugInfo/CodeView/EnumTables.h
    llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h
    llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h
    llvm/include/llvm/Support/TargetOpcodes.def
    llvm/include/llvm/Target/Target.td
    llvm/include/llvm/Target/TargetSelectionDAG.td
    llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
    llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
    llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
    llvm/lib/CodeGen/MachineCSE.cpp
    llvm/lib/CodeGen/MachineInstr.cpp
    llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
    llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
    llvm/lib/DebugInfo/CodeView/EnumTables.cpp
    llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp
    llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp
    llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
    llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp
    llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp
    llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
    llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
    llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
    llvm/lib/Target/ARM/ARMAsmPrinter.cpp
    llvm/lib/Target/ARM/ARMAsmPrinter.h
    llvm/lib/Target/X86/X86ISelLowering.cpp
    llvm/lib/Target/X86/X86ISelLowering.h
    llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt-constrain.mir
    llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt.mir
    llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 0ac497c5f8efbd..9bfb4d42a54f7f 100644
--- 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 <cstdint>
@@ -766,6 +767,18 @@ class AsmPrinter : public MachineFunctionPass {
   /// 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 <Base Address, Base Offset, Branch Address, Entry Size>
+  virtual std::tuple<const MCSymbol *, uint64_t, const MCSymbol *,
+                     codeview::JumpTableEntrySize>
+  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
index 6da28ee93dc83d..a1f49cbae3272c 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -1049,6 +1049,10 @@ enum NodeType {
   /// 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
index 311e7f4e3d2ff9..03fb15f77c65cb 100644
--- a/llvm/include/llvm/CodeGen/MachineInstr.h
+++ b/llvm/include/llvm/CodeGen/MachineInstr.h
@@ -1350,6 +1350,10 @@ class MachineInstr
     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
index ac2cf02d3be717..a7d7a72942ff4c 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -731,6 +731,7 @@ class SelectionDAG {
   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
index bda532e5761494..557c6ef03d96b9 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAGISel.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAGISel.h
@@ -328,6 +328,8 @@ class SelectionDAGISel : public MachineFunctionPass {
   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
index ce61bc76eeaf0f..be89509b4b0beb 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -5304,11 +5304,10 @@ class TargetLowering : public TargetLoweringBase {
                                           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
index a9ad99a1d0a84c..62e559e2cebaef 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/CodeView.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/CodeView.h
@@ -615,6 +615,29 @@ inline uint32_t alignOf(CodeViewContainer Container) {
     return 1;
   return 4;
 }
+
+// Corresponds to CV_armswitchtype enum.
+// This enum represents the 
diff erent 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
index 4f8ccfdd16af59..9d85acc49fa022 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def
+++ b/llvm/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def
@@ -170,7 +170,6 @@ CV_SYMBOL(S_LDATA_HLSL     , 0x1152)
 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_FILESTATIC     , 0x1153, FileStaticSym)
 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
index ec874b7ca11482..2e3be094f8a60f 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/EnumTables.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/EnumTables.h
@@ -48,6 +48,7 @@ ArrayRef<EnumEntry<uint16_t>> getTypeModifierNames();
 ArrayRef<EnumEntry<uint8_t>> getCallingConventions();
 ArrayRef<EnumEntry<uint8_t>> getFunctionOptionEnum();
 ArrayRef<EnumEntry<uint16_t>> getLabelTypeEnum();
+ArrayRef<EnumEntry<uint16_t>> 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
index 3ddcf9c5739096..3cce40dcf735a3 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h
@@ -144,6 +144,27 @@ class ScopeEndSym : public SymbolRecord {
   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
index 3c461fd9e1e926..e371fecff1234e 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h
@@ -225,6 +225,7 @@ class LVSymbolVisitor final : public SymbolVisitorCallbacks {
   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
index db7888b043b4da..63bcf9dc44251c 100644
--- a/llvm/include/llvm/Support/TargetOpcodes.def
+++ b/llvm/include/llvm/Support/TargetOpcodes.def
@@ -226,6 +226,10 @@ HANDLE_TARGET_OPCODE(ICALL_BRANCH_FUNNEL)
 // 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
index 06521fa584cb06..d384cebdddd478 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -1410,6 +1410,14 @@ def MEMBARRIER : StandardPseudoInstruction {
   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
index 6ef59cf47b839c..78a7fc76a64eaa 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -650,6 +650,9 @@ def readcyclecounter : SDNode<"ISD::READCYCLECOUNTER", SDTIntLeaf,
 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
index a39abf4a3aaf4b..6292586c5b875e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -1726,6 +1726,10 @@ void AsmPrinter::emitFunctionBody() {
       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 @@ unsigned int AsmPrinter::getUnitLengthFieldByteSize() const {
   return dwarf::getUnitLengthFieldByteSize(
       OutStreamer->getContext().getDwarfFormat());
 }
+
+std::tuple<const MCSymbol *, uint64_t, const MCSymbol *,
+           codeview::JumpTableEntrySize>
+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<MCSymbolRefExpr>(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.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index 8161de57b58e06..47a18ec89ca08c 100644
--- 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 @@ void CodeViewDebug::emitDebugInfoForFunction(const Function *GV,
     if (SP != nullptr)
       emitDebugInfoForUDTs(LocalUDTs);
 
+    emitDebugInfoForJumpTables(FI);
+
     // We're done with this function.
     emitEndSymbolRecord(SymbolKind::S_PROC_ID_END);
   }
@@ -1578,6 +1582,11 @@ void CodeViewDebug::beginFunctionImpl(const MachineFunction *MF) {
       }
     }
   }
+
+  // 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 @@ void CodeViewDebug::endFunctionImpl(const MachineFunction *MF) {
     }
   }
 
+  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 @@ void CodeViewDebug::emitDebugInfoForGlobal(const CVGlobalVariable &CVGV) {
     emitConstantSymbolRecord(DIGV->getType(), Value, QualifiedName);
   }
 }
+
+void forEachJumpTableBranch(
+    const MachineFunction *MF, bool isThumb,
+    const std::function<void(const MachineJumpTableInfo &, const MachineInstr &,
+                             int64_t)> &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-
diff erence 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<uint16_t>(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/AsmPrinter/CodeViewDebug.h b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
index 1455ac417824c8..eb274d25de9197 100644
--- 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 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
     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 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
     std::vector<std::tuple<const MCSymbol *, const MCSymbol *, const DIType *>>
         HeapAllocSites;
 
+    std::vector<JumpTableInfo> JumpTables;
+
     const MCSymbol *Begin = nullptr;
     const MCSymbol *End = nullptr;
     unsigned FuncId = 0;
@@ -478,6 +490,10 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
 
   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/MachineCSE.cpp b/llvm/lib/CodeGen/MachineCSE.cpp
index 8bd7eae1221444..89c4562e8d3803 100644
--- a/llvm/lib/CodeGen/MachineCSE.cpp
+++ b/llvm/lib/CodeGen/MachineCSE.cpp
@@ -407,7 +407,7 @@ bool MachineCSE::PhysRegDefsReach(MachineInstr *CSMI, MachineInstr *MI,
 
 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
index 1c03ac13bfb1c8..fefae658299194 100644
--- a/llvm/lib/CodeGen/MachineInstr.cpp
+++ b/llvm/lib/CodeGen/MachineInstr.cpp
@@ -1263,7 +1263,8 @@ bool MachineInstr::isSafeToMove(AAResults *AA, bool &SawStore) const {
   }
 
   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
index e3f8b30021e468..c53176e3a28327 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
@@ -3905,6 +3905,7 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
     SDValue Chain = Node->getOperand(0);
     SDValue Table = Node->getOperand(1);
     SDValue Index = Node->getOperand(2);
+    int JTI = cast<JumpTableSDNode>(Table.getNode())->getIndex();
 
     const DataLayout &TD = DAG.getDataLayout();
     EVT PTy = TLI.getPointerTy(TD);
@@ -3939,7 +3940,7 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
                           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
index 6f978339f6a8a9..006c7fe69f1230 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -1846,6 +1846,13 @@ SDValue SelectionDAG::getJumpTable(int JTI, EVT VT, bool isTarget,
   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<uint64_t>(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
index c5a269ec1b96cf..55ed461cae4fd3 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -125,6 +125,8 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
   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
index 6dc820fd2c0c7f..38b658cd7dfae8 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -2437,6 +2437,13 @@ GetVBR(uint64_t Val, const unsigned char *MatcherTable, unsigned &Idx) {
   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 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
   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
index fa030720dff33f..8d66c9f317e1ec 100644
--- 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 @@ TargetLowering::getPICJumpTableRelocBaseExpr(const MachineFunction *MF,
   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
index b2f0099bd01c5b..7e3087373bfa0b 100644
--- a/llvm/lib/DebugInfo/CodeView/EnumTables.cpp
+++ b/llvm/lib/DebugInfo/CodeView/EnumTables.cpp
@@ -434,6 +434,20 @@ static const EnumEntry<uint16_t> LabelTypeEnum[] = {
     CV_ENUM_CLASS_ENT(LabelType, Far),
 };
 
+static const EnumEntry<uint16_t> 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 @@ ArrayRef<EnumEntry<uint16_t>> getLabelTypeEnum() {
   return ArrayRef(LabelTypeEnum);
 }
 
+ArrayRef<EnumEntry<uint16_t>> 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
index cfb12dbae8457c..c86fb244f6887b 100644
--- a/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp
+++ b/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp
@@ -643,6 +643,20 @@ Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR,
   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<uint16_t>(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
index 3b627930e27167..b5e366b965a955 100644
--- a/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp
+++ b/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp
@@ -483,6 +483,19 @@ Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
   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
index 682747a2b81fe2..e903a37a8c8e08 100644
--- a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
+++ b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
@@ -442,6 +442,7 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind,
   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
index e4f5f533262bf0..fdb089c1a5f716 100644
--- a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp
@@ -1688,6 +1688,23 @@ Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
   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<uint16_t>(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
index 8d2028abfe9b41..64e1a58aa71a8a 100644
--- a/llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp
+++ b/llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp
@@ -61,6 +61,7 @@ LLVM_YAML_DECLARE_ENUM_TRAITS(CPUType)
 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<FrameCookieKind>::enumeration(
   }
 }
 
+void ScalarEnumerationTraits<JumpTableEntrySize>::enumeration(
+    IO &io, JumpTableEntrySize &FC) {
+  auto ThunkNames = getJumpTableEntrySizeNames();
+  for (const auto &E : ThunkNames) {
+    io.enumCase(FC, E.Name.str().c_str(),
+                static_cast<JumpTableEntrySize>(E.Value));
+  }
+}
+
 namespace llvm {
 namespace yaml {
 template <> struct MappingTraits<LocalVariableAddrRange> {
@@ -586,6 +596,17 @@ template <> void SymbolRecordImpl<AnnotationSym>::map(IO &IO) {
   IO.mapRequired("Strings", Symbol.Strings);
 }
 
+template <> void SymbolRecordImpl<JumpTableSym>::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
index 4d714df5bb7f60..aa7efc65949ae4 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -89,6 +89,10 @@ class AArch64AsmPrinter : public AsmPrinter {
 
   void emitStartOfAsmFile(Module &M) override;
   void emitJumpTableInfo() override;
+  std::tuple<const MCSymbol *, uint64_t, const MCSymbol *,
+             codeview::JumpTableEntrySize>
+  getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr,
+                           const MCSymbol *BranchLabel) const override;
 
   void emitFunctionEntryLabel() override;
 
@@ -1062,6 +1066,30 @@ void AArch64AsmPrinter::emitJumpTableInfo() {
   }
 }
 
+std::tuple<const MCSymbol *, uint64_t, const MCSymbol *,
+           codeview::JumpTableEntrySize>
+AArch64AsmPrinter::getCodeViewJumpTableInfo(int JTI,
+                                            const MachineInstr *BranchInstr,
+                                            const MCSymbol *BranchLabel) const {
+  const auto AFI = MF->getInfo<AArch64FunctionInfo>();
+  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
index 5c4517915edeb0..b4ad04a3315002 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -9422,8 +9422,8 @@ SDValue AArch64TargetLowering::LowerBR_JT(SDValue Op,
   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
index 2425a9a60c6373..df1695b0cc5161 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -3632,6 +3632,9 @@ bool AArch64InstructionSelector::selectBrJT(MachineInstr &I,
   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<int64_t>(JTI)});
   // Build the indirect branch.
   MIB.buildInstr(AArch64::BR, {}, {TargetReg});
   I.eraseFromParent();

diff  --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index 69df1d12aa8e28..c2aff4687d3b1f 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -1117,6 +1117,50 @@ void ARMAsmPrinter::emitJumpTableTBInst(const MachineInstr *MI,
   emitAlignment(Align(2));
 }
 
+std::tuple<const MCSymbol *, uint64_t, const MCSymbol *,
+           codeview::JumpTableEntrySize>
+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/ARM/ARMAsmPrinter.h b/llvm/lib/Target/ARM/ARMAsmPrinter.h
index bd2d9c7621196a..33b4417aa9b809 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.h
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.h
@@ -89,6 +89,10 @@ class LLVM_LIBRARY_VISIBILITY ARMAsmPrinter : public AsmPrinter {
   void emitJumpTableTBInst(const MachineInstr *MI, unsigned OffsetWidth);
   void emitInstruction(const MachineInstr *MI) override;
   bool runOnMachineFunction(MachineFunction &F) override;
+  std::tuple<const MCSymbol *, uint64_t, const MCSymbol *,
+             codeview::JumpTableEntrySize>
+  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/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 21de5e6604007c..6dcb9c7d57992a 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -56007,8 +56007,9 @@ bool X86TargetLowering::isTypeDesirableForOp(unsigned Opc, EVT VT) const {
   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 @@ SDValue X86TargetLowering::expandIndirectJTBranch(const SDLoc& dl,
     // 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/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h
index 86dfe9b52bfc0b..a7057ad7d295c0 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.h
+++ b/llvm/lib/Target/X86/X86ISelLowering.h
@@ -1562,9 +1562,8 @@ namespace llvm {
     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/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt-constrain.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt-constrain.mir
index 28160398af28fa..4ac009de19113f 100644
--- 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 @@ body:             |
   ; 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
index ba026f555b7ed3..afc5ea2e64b7e1 100644
--- 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 @@ body:             |
   ; 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
index 00000000000000..0995db038345f4
--- /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
index 00000000000000..a1f16f48962f99
--- /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
index 96c3d49072b68c..6b6ba48f9214d8 100644
--- a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
+++ b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
@@ -358,6 +358,23 @@ static std::string formatGaps(uint32_t IndentLevel,
   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 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
                                                    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();
+}


        


More information about the llvm-commits mailing list