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 <cstdint>
@@ -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 <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
   //===------------------------------------------------------------------===//
Index: llvm/include/llvm/CodeGen/ISDOpcodes.h
===================================================================
--- llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -1035,6 +1035,10 @@
   /// is the jumptable index, the last one is the jumptable entry index.
   BR_JT,
 
+  /// JUMP_TABLE_DEBUG_INFO - Jumptable debug info. The first operand is the
+  /// chain, the second is the jumptable index.
+  JUMP_TABLE_DEBUG_INFO,
+
   /// BRCOND - Conditional branch.  The first operand is the chain, the
   /// second is the condition, the third is the block to branch to if the
   /// condition is true.  If the type of the condition is not i1, then the
Index: llvm/include/llvm/CodeGen/MachineInstr.h
===================================================================
--- llvm/include/llvm/CodeGen/MachineInstr.h
+++ llvm/include/llvm/CodeGen/MachineInstr.h
@@ -1357,6 +1357,10 @@
     return false;
   }
 
+  bool isJumpTableDebugInfo() const {
+    return getOpcode() == TargetOpcode::JUMP_TABLE_DEBUG_INFO;
+  }
+
   bool isPHI() const {
     return getOpcode() == TargetOpcode::PHI ||
            getOpcode() == TargetOpcode::G_PHI;
Index: llvm/include/llvm/CodeGen/SelectionDAG.h
===================================================================
--- llvm/include/llvm/CodeGen/SelectionDAG.h
+++ llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -731,6 +731,7 @@
   SDValue getTargetJumpTable(int JTI, EVT VT, unsigned TargetFlags = 0) {
     return getJumpTable(JTI, VT, true, TargetFlags);
   }
+  SDValue getJumpTableDebugInfo(int JTI, SDValue Chain, const SDLoc &DL);
   SDValue getConstantPool(const Constant *C, EVT VT,
                           MaybeAlign Align = std::nullopt, int Offs = 0,
                           bool isT = false, unsigned TargetFlags = 0);
Index: llvm/include/llvm/CodeGen/SelectionDAGISel.h
===================================================================
--- llvm/include/llvm/CodeGen/SelectionDAGISel.h
+++ llvm/include/llvm/CodeGen/SelectionDAGISel.h
@@ -327,6 +327,8 @@
   void Select_STACKMAP(SDNode *N);
   void Select_PATCHPOINT(SDNode *N);
 
+  void Select_JUMP_TABLE_DEBUG_INFO(SDNode *N);
+
 private:
   void DoInstructionSelection();
   SDNode *MorphNode(SDNode *Node, unsigned TargetOpc, SDVTList VTList,
Index: llvm/include/llvm/CodeGen/TargetLowering.h
===================================================================
--- llvm/include/llvm/CodeGen/TargetLowering.h
+++ llvm/include/llvm/CodeGen/TargetLowering.h
@@ -5288,11 +5288,10 @@
                                           SelectionDAG &DAG) const;
 
   /// Expands target specific indirect branch for the case of JumpTable
-  /// expanasion.
-  virtual SDValue expandIndirectJTBranch(const SDLoc& dl, SDValue Value, SDValue Addr,
-                                         SelectionDAG &DAG) const {
-    return DAG.getNode(ISD::BRIND, dl, MVT::Other, Value, Addr);
-  }
+  /// expansion.
+  virtual SDValue expandIndirectJTBranch(const SDLoc &dl, SDValue Value,
+                                         SDValue Addr, int JTI,
+                                         SelectionDAG &DAG) const;
 
   // seteq(x, 0) -> truncate(srl(ctlz(zext(x)), log2(#bits)))
   // If we're comparing for equality to zero and isCtlzFast is true, expose the
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<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
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/include/llvm/Support/TargetOpcodes.def
===================================================================
--- llvm/include/llvm/Support/TargetOpcodes.def
+++ llvm/include/llvm/Support/TargetOpcodes.def
@@ -226,6 +226,10 @@
 // barrier, but does not correspond to any generated instruction.
 HANDLE_TARGET_OPCODE(MEMBARRIER)
 
+// Provides information about what jump table the following indirect branch is
+// using.
+HANDLE_TARGET_OPCODE(JUMP_TABLE_DEBUG_INFO)
+
 /// The following generic opcodes are not supposed to appear after ISel.
 /// This is something we might want to relax, but for now, this is convenient
 /// to produce diagnostics.
Index: llvm/include/llvm/Target/Target.td
===================================================================
--- llvm/include/llvm/Target/Target.td
+++ llvm/include/llvm/Target/Target.td
@@ -1410,6 +1410,14 @@
   let Size = 0;
   let isMeta = true;
 }
+def JUMP_TABLE_DEBUG_INFO : StandardPseudoInstruction {
+  let OutOperandList = (outs);
+  let InOperandList = (ins i64imm:$jti);
+  let AsmString = "";
+  let hasSideEffects = false;
+  let Size = 0;
+  let isMeta = true;
+}
 
 // Generic opcodes used in GlobalISel.
 include "llvm/Target/GenericOpcodes.td"
Index: llvm/include/llvm/Target/TargetSelectionDAG.td
===================================================================
--- llvm/include/llvm/Target/TargetSelectionDAG.td
+++ llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -640,6 +640,9 @@
 def membarrier : SDNode<"ISD::MEMBARRIER", SDTNone,
                         [SDNPHasChain, SDNPSideEffect]>;
 
+def jump_table_debug_info : SDNode<"ISD::JUMP_TABLE_DEBUG_INFO", SDTNone,
+                        [SDNPHasChain]>;
+
 def atomic_fence : SDNode<"ISD::ATOMIC_FENCE" , SDTAtomicFence,
                           [SDNPHasChain, SDNPSideEffect]>;
 
Index: llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
===================================================================
--- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -1719,6 +1719,10 @@
       case TargetOpcode::MEMBARRIER:
         OutStreamer->emitRawComment("MEMBARRIER");
         break;
+      case TargetOpcode::JUMP_TABLE_DEBUG_INFO:
+        // This instruction is only used to note jump table debug info, it's
+        // purely meta information.
+        break;
       default:
         emitInstruction(&MI);
         if (CanDoExtraAnalysis) {
@@ -4157,3 +4161,18 @@
   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);
+}
Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
===================================================================
--- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
+++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
@@ -23,6 +23,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/CodeGen/DbgEntityHistoryCalculator.h"
 #include "llvm/CodeGen/DebugHandlerBase.h"
+#include "llvm/CodeGen/MachineJumpTableInfo.h"
 #include "llvm/DebugInfo/CodeView/CodeView.h"
 #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
 #include "llvm/DebugInfo/CodeView/TypeIndex.h"
@@ -133,6 +134,15 @@
     StringRef Name;
   };
 
+  struct JumpTableInfo {
+    codeview::JumpTableEntrySize EntrySize;
+    const MCSymbol *Base;
+    uint64_t BaseOffset;
+    const MCSymbol *Branch;
+    const MCSymbol *Table;
+    size_t TableSize;
+  };
+
   // For each function, store a vector of labels to its instructions, as well as
   // to the end of the function.
   struct FunctionInfo {
@@ -160,6 +170,8 @@
     std::vector<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 @@
 
   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;
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"
@@ -1243,6 +1244,8 @@
     if (SP != nullptr)
       emitDebugInfoForUDTs(LocalUDTs);
 
+    emitDebugInfoForJumpTables(FI);
+
     // We're done with this function.
     emitEndSymbolRecord(SymbolKind::S_PROC_ID_END);
   }
@@ -1578,6 +1581,11 @@
       }
     }
   }
+
+  // Mark branches that may potentially be using jump tables with labels.
+  bool isThumb = Triple(MMI->getModule()->getTargetTriple()).getArch() ==
+                 llvm::Triple::ArchType::thumb;
+  discoverJumpTableBranches(MF, isThumb);
 }
 
 static bool shouldEmitUdt(const DIType *T) {
@@ -3083,6 +3091,10 @@
     }
   }
 
+  bool isThumb = Triple(MMI->getModule()->getTargetTriple()).getArch() ==
+                 llvm::Triple::ArchType::thumb;
+  collectDebugInfoForJumpTables(MF, isThumb);
+
   CurFn->Annotations = MF->getCodeViewAnnotations();
 
   CurFn->End = Asm->getFunctionEnd();
@@ -3442,3 +3454,126 @@
     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()) {
+    for (const auto &MBB : *MF) {
+      // Search for indirect branches...
+      const auto LastMI = MBB.getFirstTerminator();
+      if (LastMI != MBB.end() && LastMI->isIndirectBranch()) {
+        bool foundJTI = false;
+        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()) {
+              foundJTI = true;
+              Callback(*JTI, *LastMI, MO.getIndex());
+              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()) {
+              foundJTI = true;
+              Callback(*JTI, *LastMI, I->getOperand(0).getImm());
+              break;
+            }
+          }
+        }
+        assert(foundJTI);
+      }
+    }
+  }
+}
+
+void CodeViewDebug::discoverJumpTableBranches(const MachineFunction *MF,
+                                              bool isThumb) {
+  forEachJumpTableBranch(
+      MF, isThumb,
+      [this](const MachineJumpTableInfo &, const MachineInstr &BranchMI,
+             int64_t) { requestLabelBeforeInsn(&BranchMI); });
+}
+
+void CodeViewDebug::collectDebugInfoForJumpTables(const MachineFunction *MF,
+                                                  bool isThumb) {
+  forEachJumpTableBranch(
+      MF, isThumb,
+      [this, MF](const MachineJumpTableInfo &JTI, const MachineInstr &BranchMI,
+                 int64_t JumpTableIndex) {
+        // For label-difference jump tables, find the base expression.
+        // Otherwise the jump table uses an absolute address (so no base
+        // is required).
+        const MCSymbol *Base;
+        uint64_t BaseOffset = 0;
+        const MCSymbol *Branch = getLabelBeforeInsn(&BranchMI);
+        JumpTableEntrySize EntrySize;
+        switch (JTI.getEntryKind()) {
+        case MachineJumpTableInfo::EK_Custom32:
+        case MachineJumpTableInfo::EK_GPRel32BlockAddress:
+        case MachineJumpTableInfo::EK_GPRel64BlockAddress:
+          llvm_unreachable(
+              "EK_Custom32, EK_GPRel32BlockAddress, and "
+              "EK_GPRel64BlockAddress should never be emitted for COFF");
+        case MachineJumpTableInfo::EK_BlockAddress:
+          // Each entry is an absolute address.
+          EntrySize = JumpTableEntrySize::Pointer;
+          Base = nullptr;
+          break;
+        case MachineJumpTableInfo::EK_Inline:
+        case MachineJumpTableInfo::EK_LabelDifference32:
+          // Ask the AsmPrinter.
+          std::tie(Base, BaseOffset, Branch, EntrySize) =
+              Asm->getCodeViewJumpTableInfo(JumpTableIndex, &BranchMI, Branch);
+          break;
+        default:
+          llvm_unreachable("Unknown JumpTableEntryKind");
+        }
+
+        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);
+  }
+}
Index: llvm/lib/CodeGen/MachineCSE.cpp
===================================================================
--- llvm/lib/CodeGen/MachineCSE.cpp
+++ llvm/lib/CodeGen/MachineCSE.cpp
@@ -403,7 +403,7 @@
 
 bool MachineCSE::isCSECandidate(MachineInstr *MI) {
   if (MI->isPosition() || MI->isPHI() || MI->isImplicitDef() || MI->isKill() ||
-      MI->isInlineAsm() || MI->isDebugInstr())
+      MI->isInlineAsm() || MI->isDebugInstr() || MI->isJumpTableDebugInfo())
     return false;
 
   // Ignore copies.
Index: llvm/lib/CodeGen/MachineInstr.cpp
===================================================================
--- llvm/lib/CodeGen/MachineInstr.cpp
+++ llvm/lib/CodeGen/MachineInstr.cpp
@@ -1263,7 +1263,8 @@
   }
 
   if (isPosition() || isDebugInstr() || isTerminator() ||
-      mayRaiseFPException() || hasUnmodeledSideEffects())
+      mayRaiseFPException() || hasUnmodeledSideEffects() ||
+      isJumpTableDebugInfo())
     return false;
 
   // See if this instruction does a load.  If so, we have to guarantee that the
Index: llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
===================================================================
--- llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
+++ llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
@@ -3901,6 +3901,7 @@
     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);
@@ -3935,7 +3936,7 @@
                           TLI.getPICJumpTableRelocBase(Table, DAG));
     }
 
-    Tmp1 = TLI.expandIndirectJTBranch(dl, LD.getValue(1), Addr, DAG);
+    Tmp1 = TLI.expandIndirectJTBranch(dl, LD.getValue(1), Addr, JTI, DAG);
     Results.push_back(Tmp1);
     break;
   }
Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
===================================================================
--- llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -1801,6 +1801,13 @@
   return SDValue(N, 0);
 }
 
+SDValue SelectionDAG::getJumpTableDebugInfo(int JTI, SDValue Chain,
+                                            const SDLoc &DL) {
+  EVT PTy = getTargetLoweringInfo().getPointerTy(getDataLayout());
+  return getNode(ISD::JUMP_TABLE_DEBUG_INFO, DL, MVT::Glue, Chain,
+                 getTargetConstant(static_cast<uint64_t>(JTI), DL, PTy, true));
+}
+
 SDValue SelectionDAG::getConstantPool(const Constant *C, EVT VT,
                                       MaybeAlign Alignment, int Offset,
                                       bool isTarget, unsigned TargetFlags) {
Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
===================================================================
--- llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -125,6 +125,8 @@
   case ISD::GlobalTLSAddress:           return "GlobalTLSAddress";
   case ISD::FrameIndex:                 return "FrameIndex";
   case ISD::JumpTable:                  return "JumpTable";
+  case ISD::JUMP_TABLE_DEBUG_INFO:
+    return "JUMP_TABLE_DEBUG_INFO";
   case ISD::GLOBAL_OFFSET_TABLE:        return "GLOBAL_OFFSET_TABLE";
   case ISD::RETURNADDR:                 return "RETURNADDR";
   case ISD::ADDROFRETURNADDR:           return "ADDROFRETURNADDR";
Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
===================================================================
--- llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -2435,6 +2435,13 @@
   return Val;
 }
 
+void SelectionDAGISel::Select_JUMP_TABLE_DEBUG_INFO(SDNode *N) {
+  SDLoc dl(N);
+  CurDAG->SelectNodeTo(N, TargetOpcode::JUMP_TABLE_DEBUG_INFO, MVT::Glue,
+                       CurDAG->getTargetConstant(N->getConstantOperandVal(1),
+                                                 dl, MVT::i64, true));
+}
+
 /// When a match is complete, this method updates uses of interior chain results
 /// to use the new results.
 void SelectionDAGISel::UpdateChains(
@@ -2981,6 +2988,9 @@
   case ISD::PATCHPOINT:
     Select_PATCHPOINT(NodeToMatch);
     return;
+  case ISD::JUMP_TABLE_DEBUG_INFO:
+    Select_JUMP_TABLE_DEBUG_INFO(NodeToMatch);
+    return;
   }
 
   assert(!NodeToMatch->isMachineOpcode() && "Node already selected!");
Index: llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
===================================================================
--- llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -18,6 +18,7 @@
 #include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/MachineJumpTableInfo.h"
+#include "llvm/CodeGen/MachineModuleInfoImpls.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/SelectionDAG.h"
 #include "llvm/CodeGen/TargetRegisterInfo.h"
@@ -472,6 +473,17 @@
   return MCSymbolRefExpr::create(MF->getJTISymbol(JTI, Ctx), Ctx);
 }
 
+SDValue TargetLowering::expandIndirectJTBranch(const SDLoc &dl, SDValue Value,
+                                               SDValue Addr, int JTI,
+                                               SelectionDAG &DAG) const {
+  SDValue Chain = Value;
+  // Jump table debug info is only needed if CodeView is enabled.
+  if (DAG.getTarget().getTargetTriple().isOSBinFormatCOFF()) {
+    Chain = DAG.getJumpTableDebugInfo(JTI, Chain, dl);
+  }
+  return DAG.getNode(ISD::BRIND, dl, MVT::Other, Chain, Addr);
+}
+
 bool
 TargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const {
   const TargetMachine &TM = getTargetMachine();
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<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 @@
   return ArrayRef(LabelTypeEnum);
 }
 
+ArrayRef<EnumEntry<uint16_t>> 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<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();
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<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"
 
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<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 @@
   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
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<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 @@
   }
 }
 
+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() ==
Index: llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
===================================================================
--- llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -9352,8 +9352,8 @@
   SDNode *Dest =
       DAG.getMachineNode(AArch64::JumpTableDest32, DL, MVT::i64, MVT::i64, JT,
                          Entry, DAG.getTargetJumpTable(JTI, MVT::i32));
-  return DAG.getNode(ISD::BRIND, DL, MVT::Other, Op.getOperand(0),
-                     SDValue(Dest, 0));
+  SDValue JTInfo = DAG.getJumpTableDebugInfo(JTI, Op.getOperand(0), DL);
+  return DAG.getNode(ISD::BRIND, DL, MVT::Other, JTInfo, SDValue(Dest, 0));
 }
 
 SDValue AArch64TargetLowering::LowerConstantPool(SDValue Op,
Index: llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
===================================================================
--- llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -3636,6 +3636,9 @@
   auto JumpTableInst = MIB.buildInstr(AArch64::JumpTableDest32,
                                       {TargetReg, ScratchReg}, {JTAddr, Index})
                            .addJumpTableIndex(JTI);
+  // Save the jump table info.
+  MIB.buildInstr(TargetOpcode::JUMP_TABLE_DEBUG_INFO, {},
+                 {static_cast<int64_t>(JTI)});
   // Build the indirect branch.
   MIB.buildInstr(AArch64::BR, {}, {TargetReg});
   I.eraseFromParent();
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<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!
Index: llvm/lib/Target/ARM/ARMAsmPrinter.cpp
===================================================================
--- llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -1117,6 +1117,50 @@
   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");
Index: llvm/lib/Target/X86/X86ISelLowering.h
===================================================================
--- llvm/lib/Target/X86/X86ISelLowering.h
+++ llvm/lib/Target/X86/X86ISelLowering.h
@@ -1550,9 +1550,8 @@
     bool lowerInterleavedStore(StoreInst *SI, ShuffleVectorInst *SVI,
                                unsigned Factor) const override;
 
-    SDValue expandIndirectJTBranch(const SDLoc& dl, SDValue Value,
-                                   SDValue Addr, SelectionDAG &DAG)
-                                   const override;
+    SDValue expandIndirectJTBranch(const SDLoc &dl, SDValue Value, SDValue Addr,
+                                   int JTI, SelectionDAG &DAG) const override;
 
     Align getPrefLoopAlignment(MachineLoop *ML) const override;
 
Index: llvm/lib/Target/X86/X86ISelLowering.cpp
===================================================================
--- llvm/lib/Target/X86/X86ISelLowering.cpp
+++ llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -58331,8 +58331,9 @@
   return true;
 }
 
-SDValue X86TargetLowering::expandIndirectJTBranch(const SDLoc& dl,
+SDValue X86TargetLowering::expandIndirectJTBranch(const SDLoc &dl,
                                                   SDValue Value, SDValue Addr,
+                                                  int JTI,
                                                   SelectionDAG &DAG) const {
   const Module *M = DAG.getMachineFunction().getMMI().getModule();
   Metadata *IsCFProtectionSupported = M->getModuleFlag("cf-protection-branch");
@@ -58341,10 +58342,11 @@
     // notrack prefix to the indirect branch.
     // In order to do that we create NT_BRIND SDNode.
     // Upon ISEL, the pattern will convert it to jmp with NoTrack prefix.
-    return DAG.getNode(X86ISD::NT_BRIND, dl, MVT::Other, Value, Addr);
+    SDValue JTInfo = DAG.getJumpTableDebugInfo(JTI, Value, dl);
+    return DAG.getNode(X86ISD::NT_BRIND, dl, MVT::Other, JTInfo, Addr);
   }
 
-  return TargetLowering::expandIndirectJTBranch(dl, Value, Addr, DAG);
+  return TargetLowering::expandIndirectJTBranch(dl, Value, Addr, JTI, DAG);
 }
 
 TargetLowering::AndOrSETCCFoldKind
Index: llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt-constrain.mir
===================================================================
--- llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt-constrain.mir
+++ llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt-constrain.mir
@@ -35,6 +35,7 @@
   ; CHECK-NEXT: {{  $}}
   ; CHECK-NEXT:   [[MOVaddrJT:%[0-9]+]]:gpr64common = MOVaddrJT target-flags(aarch64-page) %jump-table.0, target-flags(aarch64-pageoff, aarch64-nc) %jump-table.0
   ; CHECK-NEXT:   early-clobber %5:gpr64, early-clobber %6:gpr64sp = JumpTableDest32 [[MOVaddrJT]], [[SUBREG_TO_REG]], %jump-table.0
+  ; CHECK-NEXT:   JUMP_TABLE_DEBUG_INFO 0
   ; CHECK-NEXT:   BR %5
   ; CHECK-NEXT: {{  $}}
   ; CHECK-NEXT: bb.2:
Index: llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt.mir
===================================================================
--- llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt.mir
+++ llvm/test/CodeGen/AArch64/GlobalISel/select-jump-table-brjt.mir
@@ -71,6 +71,7 @@
   ; CHECK-NEXT:   [[COPY2:%[0-9]+]]:gpr32 = COPY $wzr
   ; CHECK-NEXT:   [[MOVaddrJT:%[0-9]+]]:gpr64common = MOVaddrJT target-flags(aarch64-page) %jump-table.0, target-flags(aarch64-pageoff, aarch64-nc) %jump-table.0
   ; CHECK-NEXT:   early-clobber %17:gpr64, early-clobber %18:gpr64sp = JumpTableDest32 [[MOVaddrJT]], [[SUBREG_TO_REG]], %jump-table.0
+  ; CHECK-NEXT:   JUMP_TABLE_DEBUG_INFO 0
   ; CHECK-NEXT:   BR %17
   ; CHECK-NEXT: {{  $}}
   ; CHECK-NEXT: bb.2.sw.bb:
Index: llvm/test/DebugInfo/COFF/jump-table.ll
===================================================================
--- /dev/null
+++ llvm/test/DebugInfo/COFF/jump-table.ll
@@ -0,0 +1,259 @@
+; 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)
Index: llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
===================================================================
--- llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
+++ llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
@@ -358,6 +358,23 @@
   return typesetItemList(GapStrs, 7, IndentLevel, ", ");
 }
 
+static std::string formatJumpTableEntrySize(JumpTableEntrySize EntrySize) {
+  switch (EntrySize) {
+    RETURN_CASE(JumpTableEntrySize, Int8, "int8");
+    RETURN_CASE(JumpTableEntrySize, UInt8, "uin8");
+    RETURN_CASE(JumpTableEntrySize, Int16, "int16");
+    RETURN_CASE(JumpTableEntrySize, UInt16, "uint16");
+    RETURN_CASE(JumpTableEntrySize, Int32, "int32");
+    RETURN_CASE(JumpTableEntrySize, UInt32, "uint32");
+    RETURN_CASE(JumpTableEntrySize, Pointer, "pointer");
+    RETURN_CASE(JumpTableEntrySize, UInt8ShiftLeft, "uint8shl");
+    RETURN_CASE(JumpTableEntrySize, UInt16ShiftLeft, "uint16shl");
+    RETURN_CASE(JumpTableEntrySize, Int8ShiftLeft, "int8shl");
+    RETURN_CASE(JumpTableEntrySize, Int16ShiftLeft, "int16shl");
+  }
+  return formatUnknownEnum(EntrySize);
+}
+
 Error MinimalSymbolDumper::visitSymbolBegin(codeview::CVSymbol &Record) {
   return visitSymbolBegin(Record, 0);
 }
@@ -905,3 +922,17 @@
                                                    Annot.Strings));
   return Error::success();
 }
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+                                            JumpTableSym &JumpTable) {
+  AutoIndent Indent(P, 7);
+  P.formatLine(
+      "base = {0}, switchtype = {1}, branch = {2}, table = {3}, entriescount = "
+      "{4}",
+      formatSegmentOffset(JumpTable.BaseSegment, JumpTable.BaseOffset),
+      formatJumpTableEntrySize(JumpTable.SwitchType),
+      formatSegmentOffset(JumpTable.BranchSegment, JumpTable.BranchOffset),
+      formatSegmentOffset(JumpTable.TableSegment, JumpTable.TableOffset),
+      JumpTable.EntriesCount);
+  return Error::success();
+}