[llvm] [DebugInfo] Support global addresses in variable locations (PR #218722)

Adrian Prantl via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 9 17:05:28 PDT 2026


https://github.com/adrian-prantl updated https://github.com/llvm/llvm-project/pull/218722

>From 6f54f597bfe9e07fa1d6c5c1a2c772a1bc302fcb Mon Sep 17 00:00:00 2001
From: Adrian Prantl <aprantl at apple.com>
Date: Tue, 25 Aug 2026 09:22:34 -0700
Subject: [PATCH 1/5] [DebugInfo] Support global addresses in variable
 locations

This patch adds support for global address locations to SelectionDAG,
GlobalISel and AsmPrinter. The actual implementation is very similar
to how constants are being handled so most code changes are very
mechanical.

The motivation for adding global address support is the Swift compiler
and it is needed even in unoptimized code. For example, the standard
libary implements the empty array with a global "empty array storage"
singleton object, so it can show up as the location of array
values. Another very common use-case are references to type metadata,
which is also accessed via global symbols.

It is even possible to motivate this with a C exmple:

  extern int g; int *p = &g;

also produces the dbg_value with a global value.

This patch adds an SDDbgOperand kind for a global address which gets
lowered to a MO_GlobalAddress DBG_VALUE operand, a DbgValueLocEntry,
and finally DW_OP_addrx/DW_OP_stack_value, similar to how global
DITemplateValueParameter are handled.

In AsmPrinter the address pool is preferred, its index is plain data
that both a DIE and the byte stream backing a location list can hold.
Before DWARF 5 the pool is only available under split DWARF, so fall
back to a relocated DW_OP_addr, which only a DIE can carry. Emit no
location at all when neither is possible.

Not handled are thread locals, which need DW_OP_form_tls_address,
dllimport, whose address requires a load from the import address
table, and ifuncs, whose symbol address is not the value of the
pointer.

rdar://156549577
Assisted-by: Claude
---
 llvm/include/llvm/CodeGen/Analysis.h          |  6 ++
 llvm/lib/CodeGen/Analysis.cpp                 | 15 +++
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp    |  6 ++
 llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h   | 12 ++-
 .../CodeGen/AsmPrinter/DwarfCompileUnit.cpp   | 12 +++
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp    | 12 ++-
 .../CodeGen/AsmPrinter/DwarfExpression.cpp    | 26 +++++
 llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h | 20 ++++
 llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp     |  4 +
 .../CodeGen/GlobalISel/MachineIRBuilder.cpp   | 17 +++-
 .../LiveDebugValues/InstrRefBasedImpl.cpp     |  2 +-
 .../LiveDebugValues/VarLocBasedImpl.cpp       | 20 +++-
 .../lib/CodeGen/SelectionDAG/InstrEmitter.cpp | 10 +-
 .../lib/CodeGen/SelectionDAG/SDNodeDbgValue.h | 37 +++++--
 .../SelectionDAG/SelectionDAGBuilder.cpp      |  7 ++
 .../CodeGen/AArch64/GlobalISel/debug-insts.ll |  2 +-
 .../AArch64/dbg-value-globaladdr-gisel.ll     | 89 +++++++++++++++++
 .../DebugInfo/X86/dbg-val-list-dangling.ll    | 12 +--
 .../DebugInfo/X86/dbg-value-globaladdr.ll     | 97 +++++++++++++++++++
 .../DebugInfo/X86/sdag-dangling-dbgvalue.ll   | 61 +++++-------
 .../X86/sdag-dangling-dbgassign.ll            | 61 +++++-------
 21 files changed, 430 insertions(+), 98 deletions(-)
 create mode 100644 llvm/test/DebugInfo/AArch64/dbg-value-globaladdr-gisel.ll
 create mode 100644 llvm/test/DebugInfo/X86/dbg-value-globaladdr.ll

diff --git a/llvm/include/llvm/CodeGen/Analysis.h b/llvm/include/llvm/CodeGen/Analysis.h
index c2bcb93c649b4..99641a3e1290c 100644
--- a/llvm/include/llvm/CodeGen/Analysis.h
+++ b/llvm/include/llvm/CodeGen/Analysis.h
@@ -116,6 +116,12 @@ LLVM_ABI ISD::CondCode getICmpCondCode(ICmpInst::Predicate Pred);
 /// corresponding to the given ISD integer condition code.
 LLVM_ABI ICmpInst::Predicate getICmpCondCode(ISD::CondCode Pred);
 
+/// Test if the address of \p GV can be described in debug info as a plain
+/// reference to its symbol, so that a variable holding that address can be
+/// described for the whole of its scope rather than only from wherever the
+/// address happens to be materialized.
+LLVM_ABI bool canDescribeGlobalAddressInDebugInfo(const GlobalValue *GV);
+
 /// Test if the given instruction is in a position to be optimized
 /// with a tail-call. This roughly means that it's in a block with
 /// a return and there's nothing that needs to be scheduled
diff --git a/llvm/lib/CodeGen/Analysis.cpp b/llvm/lib/CodeGen/Analysis.cpp
index 9ece9f0c187a2..ff643b77678be 100644
--- a/llvm/lib/CodeGen/Analysis.cpp
+++ b/llvm/lib/CodeGen/Analysis.cpp
@@ -529,6 +529,21 @@ static bool nextRealType(SmallVectorImpl<Type *> &SubTypes,
   return true;
 }
 
+bool llvm::canDescribeGlobalAddressInDebugInfo(const GlobalValue *GV) {
+  // A thread-local's address is not known until it is resolved against a
+  // thread's storage, which a plain symbol reference cannot express.
+  if (GV->isThreadLocal())
+    return false;
+  // Computing the address of a dllimport'd entity requires a load from the
+  // import address table, which a static symbol reference cannot express.
+  if (GV->hasDLLImportStorageClass())
+    return false;
+  // An ifunc resolves to whatever its resolver returns at load time, so the
+  // symbol's own address is not the value of the pointer.
+  if (isa<GlobalIFunc>(GV))
+    return false;
+  return true;
+}
 
 /// Test if the given instruction is in a position to be optimized
 /// with a tail-call. This roughly means that it's in a block with
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index cbd8f1932a285..046ebd43a7fe0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -1343,6 +1343,12 @@ static bool emitDebugValueComment(const MachineInstr *MI, AsmPrinter &AP) {
       OS << "!target-index(" << Op.getIndex() << "," << Op.getOffset() << ")";
       break;
     }
+    case MachineOperand::MO_GlobalAddress: {
+      Op.getGlobal()->printAsOperand(OS, /*PrintType=*/false);
+      if (Op.getOffset())
+        OS << '+' << Op.getOffset();
+      break;
+    }
     case MachineOperand::MO_Register:
     case MachineOperand::MO_FrameIndex: {
       Register Reg;
diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h
index 5358f7b54f411..13621fb9ac9b9 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h
@@ -19,6 +19,7 @@
 
 namespace llvm {
 class AsmPrinter;
+class GlobalValue;
 
 /// This struct describes target specific location.
 struct TargetIndexLocation {
@@ -45,7 +46,8 @@ class DbgValueLocEntry {
     E_Integer,
     E_ConstantFP,
     E_ConstantInt,
-    E_TargetIndexLocation
+    E_TargetIndexLocation,
+    E_GlobalAddress
   };
   enum EntryType EntryKind;
 
@@ -54,6 +56,7 @@ class DbgValueLocEntry {
     int64_t Int;
     const ConstantFP *CFP;
     const ConstantInt *CIP;
+    const GlobalValue *GV;
   } Constant;
 
   union {
@@ -74,6 +77,9 @@ class DbgValueLocEntry {
   DbgValueLocEntry(MachineLocation Loc) : EntryKind(E_Location), Loc(Loc) {}
   DbgValueLocEntry(TargetIndexLocation Loc)
       : EntryKind(E_TargetIndexLocation), TIL(Loc) {}
+  DbgValueLocEntry(const GlobalValue *GV) : EntryKind(E_GlobalAddress) {
+    Constant.GV = GV;
+  }
 
   bool isLocation() const { return EntryKind == E_Location; }
   bool isIndirectLocation() const {
@@ -85,9 +91,11 @@ class DbgValueLocEntry {
   bool isInt() const { return EntryKind == E_Integer; }
   bool isConstantFP() const { return EntryKind == E_ConstantFP; }
   bool isConstantInt() const { return EntryKind == E_ConstantInt; }
+  bool isGlobalAddress() const { return EntryKind == E_GlobalAddress; }
   int64_t getInt() const { return Constant.Int; }
   const ConstantFP *getConstantFP() const { return Constant.CFP; }
   const ConstantInt *getConstantInt() const { return Constant.CIP; }
+  const GlobalValue *getGlobalAddress() const { return Constant.GV; }
   MachineLocation getLoc() const { return Loc; }
   TargetIndexLocation getTargetIndexLocation() const { return TIL; }
   friend bool operator==(const DbgValueLocEntry &, const DbgValueLocEntry &);
@@ -266,6 +274,8 @@ inline bool operator==(const DbgValueLocEntry &A, const DbgValueLocEntry &B) {
     return A.Constant.CFP == B.Constant.CFP;
   case DbgValueLocEntry::E_ConstantInt:
     return A.Constant.CIP == B.Constant.CIP;
+  case DbgValueLocEntry::E_GlobalAddress:
+    return A.Constant.GV == B.Constant.GV;
   }
   llvm_unreachable("unhandled EntryKind");
 }
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 752dc69b3971e..cf395846a59d0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -923,6 +923,15 @@ void DwarfCompileUnit::applyConcreteDbgVariableAttributes(
       addConstantFPValue(VariableDie, Entry->getConstantFP());
     } else if (Entry->isConstantInt()) {
       addConstantValue(VariableDie, Entry->getConstantInt(), DV.getType());
+    } else if (Entry->isGlobalAddress()) {
+      auto *Expr = Single.getExpr();
+      DIELoc *Loc = new (DIEValueAllocator) DIELoc;
+      DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);
+      DwarfExpr.addFragmentOffset(Expr);
+      if (!DwarfExpr.addGlobalAddress(Entry->getGlobalAddress()))
+        return;
+      DwarfExpr.addExpression(Expr);
+      addBlock(VariableDie, dwarf::DW_AT_location, DwarfExpr.finalize());
     } else if (Entry->isTargetIndexLocation()) {
       DIELoc *Loc = new (DIEValueAllocator) DIELoc;
       DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);
@@ -976,6 +985,9 @@ void DwarfCompileUnit::applyConcreteDbgVariableAttributes(
       // only the WebAssembly-specific encoding is supported.
       assert(Asm->TM.getTargetTriple().isWasm());
       DwarfExpr.addWasmLocation(Loc.Index, static_cast<uint64_t>(Loc.Offset));
+    } else if (Entry.isGlobalAddress()) {
+      if (!DwarfExpr.addGlobalAddress(Entry.getGlobalAddress()))
+        return false;
     } else {
       llvm_unreachable("Unsupported Entry type.");
     }
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 4b0c28c4649ab..14a872af897e7 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -258,6 +258,8 @@ static DbgValueLoc getDebugLocValue(const MachineInstr *MI) {
     } else if (Op.isTargetIndex()) {
       DbgValueLocEntries.push_back(
           DbgValueLocEntry(TargetIndexLocation(Op.getIndex(), Op.getOffset())));
+    } else if (Op.isGlobal()) {
+      DbgValueLocEntries.push_back(DbgValueLocEntry(Op.getGlobal()));
     } else if (Op.isImm())
       DbgValueLocEntries.push_back(DbgValueLocEntry(Op.getImm()));
     else if (Op.isFPImm())
@@ -1784,9 +1786,12 @@ static bool validThroughout(LexicalScopes &LScopes,
   // throughout the function. This is a hack, presumably for DWARF v2 and not
   // necessarily correct. It would be much better to use a dbg.declare instead
   // if we know the constant is live throughout the scope.
+  // The address of a global is a link-time constant, so for those this is not
+  // a hack: the location genuinely does describe the variable throughout.
   if (MBB->pred_empty() &&
-      all_of(DbgValue->debug_operands(),
-             [](const MachineOperand &Op) { return Op.isImm(); }))
+      all_of(DbgValue->debug_operands(), [](const MachineOperand &Op) {
+        return Op.isImm() || Op.isGlobal();
+      }))
     return true;
 
   // Test if the location terminates before the end of the scope.
@@ -3341,6 +3346,9 @@ void DwarfDebug::emitDebugLocValue(const AsmPrinter &AP, const DIBasicType *BT,
       // WebAssembly-specific encoding is supported.
       assert(AP.TM.getTargetTriple().isWasm());
       DwarfExpr.addWasmLocation(Loc.Index, static_cast<uint64_t>(Loc.Offset));
+    } else if (Entry.isGlobalAddress()) {
+      if (!DwarfExpr.addGlobalAddress(Entry.getGlobalAddress()))
+        return false;
     } else if (Entry.isConstantFP()) {
       if (AP.getDwarfVersion() >= 4 && !AP.getDwarfDebug()->tuneForSCE() &&
           !Cursor) {
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
index 2e022f30842e7..d1952555ef76f 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
@@ -862,6 +862,32 @@ void DwarfExpression::emitLegacyZExt(unsigned FromBits) {
   emitOp(dwarf::DW_OP_and);
 }
 
+bool DwarfExpression::addGlobalAddress(const GlobalValue *GV) {
+  DwarfDebug &DD = CU.getDwarfDebug();
+
+  // Prefer the address pool, whose index is plain data and so can be emitted
+  // into either output form. Before DWARF 5 the pool is only available under
+  // split DWARF, leaving a relocated DW_OP_addr as the only spelling -- which
+  // only a DIE can carry.
+  bool UsePool = DwarfVersion >= 5 || DD.useSplitDwarf();
+  if (!UsePool && !supportsRelocatedAddress())
+    return false;
+
+  assert(isImplicitLocation() || isUnknownLocation());
+  LocationKind = Implicit;
+
+  const MCSymbol *Sym = CU.getAsmPrinter()->getSymbol(GV);
+  if (UsePool) {
+    emitOp(DwarfVersion >= 5 ? dwarf::DW_OP_addrx
+                             : dwarf::DW_OP_GNU_addr_index);
+    emitUnsigned(DD.getAddressPool().getIndex(Sym));
+  } else {
+    emitOp(dwarf::DW_OP_addr);
+    emitRelocatedAddress(Sym);
+  }
+  return true;
+}
+
 void DwarfExpression::addWasmLocation(unsigned Index, uint64_t Offset) {
   emitOp(dwarf::DW_OP_WASM_location);
   emitUnsigned(Index == 4/*TI_LOCAL_INDIRECT*/ ? 0/*TI_LOCAL*/ : Index);
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
index c6f3ab432e3f4..60dc8f0cadaf1 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
@@ -27,6 +27,7 @@ class AsmPrinter;
 class APInt;
 class DwarfCompileUnit;
 class DIELoc;
+class GlobalValue;
 class TargetRegisterInfo;
 class MachineLocation;
 
@@ -136,6 +137,17 @@ class DwarfExpression {
 
   virtual void emitBaseTypeRef(uint64_t Idx) = 0;
 
+  /// Whether a relocated address operand, as needed by DW_OP_addr, can be
+  /// emitted into this output form. A location list is backed by a plain byte
+  /// buffer, which cannot carry a relocation.
+  virtual bool supportsRelocatedAddress() const { return false; }
+
+  /// Emit a relocated address operand. Only called when
+  /// supportsRelocatedAddress() returns true.
+  virtual void emitRelocatedAddress(const MCSymbol *Sym) {
+    llvm_unreachable("relocated address unsupported by this output form");
+  }
+
   /// Start emitting data to the temporary buffer. The data stored in the
   /// temporary buffer can be committed to the main output using
   /// commitTemporaryBuffer().
@@ -307,6 +319,11 @@ class DwarfExpression {
   /// Emit location information expressed via WebAssembly location + offset
   /// The Index is an identifier for locals, globals or operand stack.
   void addWasmLocation(unsigned Index, uint64_t Offset);
+
+  /// Emit the address of \p GV as an implicit location description, i.e. as the
+  /// value of the described entity rather than as the address of its storage.
+  /// Returns false if the address cannot be spelled in this unit's DWARF.
+  bool addGlobalAddress(const GlobalValue *GV);
 };
 
 /// DwarfExpression implementation for .debug_loc entries.
@@ -363,6 +380,9 @@ class DIEDwarfExpression final : public DwarfExpression {
   void emitData1(uint8_t Value) override;
   void emitBaseTypeRef(uint64_t Idx) override;
 
+  bool supportsRelocatedAddress() const override { return true; }
+  void emitRelocatedAddress(const MCSymbol *Sym) override;
+
   void enableTemporaryBuffer() override;
   void disableTemporaryBuffer() override;
   unsigned getTemporaryBufferSize() override;
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index 9ecf5b382533b..35c451618dfb8 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -61,6 +61,10 @@ void DIEDwarfExpression::emitBaseTypeRef(uint64_t Idx) {
   CU.addBaseTypeRef(getActiveDIE(), Idx);
 }
 
+void DIEDwarfExpression::emitRelocatedAddress(const MCSymbol *Sym) {
+  CU.addLabel(getActiveDIE(), dwarf::DW_FORM_addr, Sym);
+}
+
 void DIEDwarfExpression::enableTemporaryBuffer() {
   assert(!IsBuffering && "Already buffering?");
   IsBuffering = true;
diff --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
index 8237710380946..0fd4ba9efbb97 100644
--- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
@@ -9,6 +9,7 @@
 /// This file implements the MachineIRBuidler class.
 //===----------------------------------------------------------------------===//
 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/Analysis.h"
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/MachineInstr.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
@@ -108,6 +109,7 @@ MachineInstrBuilder MachineIRBuilder::buildConstDbgValue(const Constant &C,
     return &C;
   }();
 
+  bool IsIndirect = true;
   if (auto *CI = dyn_cast<ConstantInt>(NumericConstant)) {
     if (CI->getBitWidth() > 64)
       MIB.addCImm(CI);
@@ -119,12 +121,25 @@ MachineInstrBuilder MachineIRBuilder::buildConstDbgValue(const Constant &C,
     MIB.addFPImm(CFP);
   } else if (isa<ConstantPointerNull>(NumericConstant)) {
     MIB.addImm(0);
+  } else if (auto *GV = dyn_cast<GlobalValue>(NumericConstant);
+             GV && canDescribeGlobalAddressInDebugInfo(GV)) {
+    // The address of a global is a direct link-time constant.
+    MIB.addGlobalAddress(GV);
+    IsIndirect = false;
   } else {
     // Insert $noreg if we didn't find a usable constant and had to drop it.
     MIB.addReg(Register());
   }
 
-  MIB.addImm(0).addMetadata(Variable).addMetadata(Expr);
+  // DBG_VALUE spells an indirect location with a zero immediate offset operand
+  // and a direct one with $noreg. isIndirectDebugValue() ignores the offset for
+  // a non-register location operand, but isDebugOffsetImm() does not, and
+  // several consumers ask that instead.
+  if (IsIndirect)
+    MIB.addImm(0);
+  else
+    MIB.addReg(Register());
+  MIB.addMetadata(Variable).addMetadata(Expr);
   return insertInstr(MIB);
 }
 
diff --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
index 1b760a70608dd..c9d8c3b12427f 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
@@ -1466,7 +1466,7 @@ bool InstrRefBasedLDV::transferDebugValue(const MachineInstr &MI) {
         // debug values.
         if (MO.isReg()) {
           DebugOps.push_back(DbgOpStore.insert(MTracker->readReg(MO.getReg())));
-        } else if (MO.isImm() || MO.isFPImm() || MO.isCImm()) {
+        } else if (MO.isImm() || MO.isFPImm() || MO.isCImm() || MO.isGlobal()) {
           DebugOps.push_back(DbgOpStore.insert(MO));
         } else {
           llvm_unreachable("Unexpected debug operand type.");
diff --git a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp
index bdfd3bda1f4cb..7cc0b117057e6 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp
@@ -341,7 +341,8 @@ class VarLocBasedLDV : public LDVImpl {
       RegisterKind,
       SpillLocKind,
       ImmediateKind,
-      WasmLocKind
+      WasmLocKind,
+      GlobalAddrKind
     };
 
     enum class EntryValueLocKind {
@@ -361,6 +362,7 @@ class VarLocBasedLDV : public LDVImpl {
       const ConstantFP *FPImm;
       const ConstantInt *CImm;
       WasmLoc WasmLocation;
+      const GlobalValue *GV;
       MachineLocValue() : Hash(0) {}
     };
 
@@ -381,6 +383,7 @@ class VarLocBasedLDV : public LDVImpl {
           return Value.WasmLocation == Other.Value.WasmLocation;
         case MachineLocKind::RegisterKind:
         case MachineLocKind::ImmediateKind:
+        case MachineLocKind::GlobalAddrKind:
           return Value.Hash == Other.Value.Hash;
         default:
           llvm_unreachable("Invalid kind");
@@ -404,6 +407,7 @@ class VarLocBasedLDV : public LDVImpl {
                                  Other.Value.WasmLocation.Offset);
         case MachineLocKind::RegisterKind:
         case MachineLocKind::ImmediateKind:
+        case MachineLocKind::GlobalAddrKind:
           return std::tie(Kind, Value.Hash) <
                  std::tie(Other.Kind, Other.Value.Hash);
         default:
@@ -468,6 +472,9 @@ class VarLocBasedLDV : public LDVImpl {
       } else if (Op.isTargetIndex()) {
         Kind = MachineLocKind::WasmLocKind;
         Loc.WasmLocation = {Op.getIndex(), Op.getOffset()};
+      } else if (Op.isGlobal()) {
+        Kind = MachineLocKind::GlobalAddrKind;
+        Loc.GV = Op.getGlobal();
       } else
         llvm_unreachable("Invalid Op kind for MachineLoc.");
       return {Kind, Loc};
@@ -601,7 +608,8 @@ class VarLocBasedLDV : public LDVImpl {
           MOs.push_back(Orig);
           break;
         }
-        case MachineLocKind::WasmLocKind: {
+        case MachineLocKind::WasmLocKind:
+        case MachineLocKind::GlobalAddrKind: {
           MOs.push_back(Orig);
           break;
         }
@@ -614,7 +622,8 @@ class VarLocBasedLDV : public LDVImpl {
 
     /// Is the Loc field a constant or constant object?
     bool isConstant(MachineLocKind Kind) const {
-      return Kind == MachineLocKind::ImmediateKind;
+      return Kind == MachineLocKind::ImmediateKind ||
+             Kind == MachineLocKind::GlobalAddrKind;
     }
 
     /// Check if the Loc field is an entry backup location.
@@ -733,6 +742,9 @@ class VarLocBasedLDV : public LDVImpl {
         case MachineLocKind::ImmediateKind:
           Out << MLoc.Value.Immediate;
           break;
+        case MachineLocKind::GlobalAddrKind:
+          Out << MLoc.Value.GV->getName();
+          break;
         case MachineLocKind::WasmLocKind: {
           if (TII) {
             auto Indices = TII->getSerializableTargetIndices();
@@ -1432,7 +1444,7 @@ void VarLocBasedLDV::transferDebugValue(const MachineInstr &MI,
 
   if (all_of(MI.debug_operands(), [](const MachineOperand &MO) {
         return (MO.isReg() && MO.getReg()) || MO.isImm() || MO.isFPImm() ||
-               MO.isCImm() || MO.isTargetIndex();
+               MO.isCImm() || MO.isTargetIndex() || MO.isGlobal();
       })) {
     // Use normal VarLoc constructor for registers and immediates.
     VarLoc VL(MI);
diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
index 8da255cda656d..67cd9cb47e264 100644
--- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
@@ -785,6 +785,9 @@ void InstrEmitter::AddDbgValueLocationOps(
     case SDDbgOperand::CONST:
       MIB.add(GetMOForConstDbgOp(Op));
       break;
+    case SDDbgOperand::GLOBALADDR:
+      MIB.addGlobalAddress(Op.getGlobal());
+      break;
     }
   }
 }
@@ -805,7 +808,8 @@ InstrEmitter::EmitDbgInstrRef(SDDbgValue *SD,
   // Returns true if the given operand is not itself an instruction reference
   // but is a legal debug operand for a DBG_INSTR_REF.
   auto IsNonInstrRefOp = [](SDDbgOperand DbgOp) {
-    return DbgOp.getKind() == SDDbgOperand::CONST;
+    return DbgOp.getKind() == SDDbgOperand::CONST ||
+           DbgOp.getKind() == SDDbgOperand::GLOBALADDR;
   };
 
   // If this variable location does not depend on any instructions or contains
@@ -883,6 +887,10 @@ InstrEmitter::EmitDbgInstrRef(SDDbgValue *SD,
       }
 
       DefMI = &*MRI->def_instr_begin(VReg);
+    } else if (DbgOperand.getKind() == SDDbgOperand::GLOBALADDR) {
+      MOs.push_back(MachineOperand::CreateGA(DbgOperand.getGlobal(),
+                                             /*Offset=*/0));
+      continue;
     } else {
       assert(DbgOperand.getKind() == SDDbgOperand::CONST);
       MOs.push_back(GetMOForConstDbgOp(DbgOperand));
diff --git a/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h b/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h
index f631d84f73082..3f58f95edbe4f 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h
+++ b/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h
@@ -23,19 +23,22 @@ namespace llvm {
 
 class DIVariable;
 class DIExpression;
+class GlobalValue;
 class SDNode;
 class Value;
 class raw_ostream;
 
 /// Holds the information for a single machine location through SDISel; either
-/// an SDNode, a constant, a stack location, or a virtual register.
+/// an SDNode, a constant, a stack location, a virtual register, or the address
+/// of a global.
 class SDDbgOperand {
 public:
   enum Kind {
-    SDNODE = 0,  ///< Value is the result of an expression.
-    CONST = 1,   ///< Value is a constant.
-    FRAMEIX = 2, ///< Value is contents of a stack location.
-    VREG = 3     ///< Value is a virtual register.
+    SDNODE = 0,    ///< Value is the result of an expression.
+    CONST = 1,     ///< Value is a constant.
+    FRAMEIX = 2,   ///< Value is contents of a stack location.
+    VREG = 3,      ///< Value is a virtual register.
+    GLOBALADDR = 4 ///< Value is the address of a global.
   };
   Kind getKind() const { return kind; }
 
@@ -69,6 +72,12 @@ class SDDbgOperand {
     return u.VReg;
   }
 
+  /// Returns the GlobalValue whose address describes the variable.
+  const GlobalValue *getGlobal() const {
+    assert(kind == GLOBALADDR);
+    return u.GA;
+  }
+
   static SDDbgOperand fromNode(SDNode *Node, unsigned ResNo) {
     return SDDbgOperand(Node, ResNo);
   }
@@ -81,6 +90,9 @@ class SDDbgOperand {
   static SDDbgOperand fromConst(const Value *Const) {
     return SDDbgOperand(Const);
   }
+  static SDDbgOperand fromGlobalAddr(const GlobalValue *GV) {
+    return SDDbgOperand(GV, GLOBALADDR);
+  }
 
   bool operator!=(const SDDbgOperand &Other) const { return !(*this == Other); }
   bool operator==(const SDDbgOperand &Other) const {
@@ -95,6 +107,8 @@ class SDDbgOperand {
       return getVReg() == Other.getVReg();
     case FRAMEIX:
       return getFrameIx() == Other.getFrameIx();
+    case GLOBALADDR:
+      return getGlobal() == Other.getGlobal();
     }
     return false;
   }
@@ -106,9 +120,10 @@ class SDDbgOperand {
       SDNode *Node;   ///< Valid for expressions.
       unsigned ResNo; ///< Valid for expressions.
     } s;
-    const Value *Const; ///< Valid for constants.
-    unsigned FrameIx;   ///< Valid for stack objects.
-    unsigned VReg;      ///< Valid for registers.
+    const Value *Const;    ///< Valid for constants.
+    unsigned FrameIx;      ///< Valid for stack objects.
+    unsigned VReg;         ///< Valid for registers.
+    const GlobalValue *GA; ///< Valid for global addresses.
   } u;
 
   /// Constructor for non-constants.
@@ -118,6 +133,12 @@ class SDDbgOperand {
   }
   /// Constructor for constants.
   SDDbgOperand(const Value *C) : kind(CONST) { u.Const = C; }
+  /// Constructor for global addresses. Takes an explicit Kind because a
+  /// GlobalValue would otherwise be ambiguous with the Value constructor.
+  SDDbgOperand(const GlobalValue *GV, Kind Kind) : kind(Kind) {
+    assert(Kind == GLOBALADDR && "Invalid SDDbgValue constructor");
+    u.GA = GV;
+  }
   /// Constructor for virtual registers and frame indices.
   SDDbgOperand(unsigned VRegOrFrameIdx, Kind Kind) : kind(Kind) {
     assert((Kind == VREG || Kind == FRAMEIX) &&
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 4f990e1ff5b9e..ef5c6832e5fe7 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -1656,6 +1656,13 @@ bool SelectionDAGBuilder::handleDebugValue(ArrayRef<const Value *> Values,
         continue;
       }
 
+    // The address of a global is a link-time constant.
+    if (const auto *GV = dyn_cast<GlobalValue>(V))
+      if (canDescribeGlobalAddressInDebugInfo(GV)) {
+        LocationOps.emplace_back(SDDbgOperand::fromGlobalAddr(GV));
+        continue;
+      }
+
     // If the Value is a frame index, we can create a FrameIndex debug value
     // without relying on the DAG at all.
     if (const AllocaInst *AI = dyn_cast<AllocaInst>(V)) {
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/debug-insts.ll b/llvm/test/CodeGen/AArch64/GlobalISel/debug-insts.ll
index d24d1f822e4ac..823824ec95fdd 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/debug-insts.ll
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/debug-insts.ll
@@ -46,7 +46,7 @@ define void @debug_value(i32 %in) #0 !dbg !16 {
   call void @llvm.dbg.value(metadata float 1.000000e+00, i64 0, metadata !17, metadata !DIExpression()), !dbg !18
 ; CHECK: DBG_VALUE 0, 0, !17, !DIExpression(), debug-location !18
   call void @llvm.dbg.value(metadata ptr null, i64 0, metadata !17, metadata !DIExpression()), !dbg !18
-; CHECK: DBG_VALUE $noreg, 0, !17, !DIExpression(), debug-location !18
+; CHECK: DBG_VALUE @gv, $noreg, !17, !DIExpression(), debug-location !18
   call void @llvm.dbg.value(metadata ptr @gv, i64 0, metadata !17, metadata !DIExpression()), !dbg !18
 ; CHECK: DBG_VALUE 42, 0, !17, !DIExpression(), debug-location !18
   call void @llvm.dbg.value(metadata ptr inttoptr (i64 42 to ptr), i64 0, metadata !17, metadata !DIExpression()), !dbg !18
diff --git a/llvm/test/DebugInfo/AArch64/dbg-value-globaladdr-gisel.ll b/llvm/test/DebugInfo/AArch64/dbg-value-globaladdr-gisel.ll
new file mode 100644
index 0000000000000..99f38390c8165
--- /dev/null
+++ b/llvm/test/DebugInfo/AArch64/dbg-value-globaladdr-gisel.ll
@@ -0,0 +1,89 @@
+;; Check that GlobalISel describes a variable holding the address of a global by
+;; naming the symbol, matching the SelectionDAG path in
+;; DebugInfo/X86/dbg-value-globaladdr.ll. The IRTranslator builds the same
+;; DBG_VALUE, and LiveDebugValues and the DWARF emitter are shared, so the
+;; emitted DWARF is identical between the two pipelines.
+
+; RUN: llc -O2 -mtriple=aarch64-apple-macosx -global-isel -stop-after=irtranslator \
+; RUN:   < %s | FileCheck %s --check-prefix=MIR
+; RUN: llc -O2 -mtriple=aarch64-apple-macosx -global-isel -filetype=obj < %s \
+; RUN:   | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF
+
+ at g = external global i64, align 8
+ at tls = external thread_local global i64, align 8
+
+;; Nothing in the function materializes the address. GlobalISel has no dangling
+;; debug info recovery at all, so before this the variable was dropped outright.
+; MIR-LABEL: name: global_only
+; MIR: DBG_VALUE @g, $noreg, ![[#]], !DIExpression()
+;
+; DWARF-LABEL: DW_AT_name ("global_only")
+; DWARF: DW_TAG_variable
+; DWARF-NEXT: DW_AT_location (DW_OP_addrx 0x1, DW_OP_stack_value)
+; DWARF-NEXT: DW_AT_name ("x")
+define void @global_only() !dbg !6 {
+entry:
+    #dbg_value(ptr @g, !10, !DIExpression(), !11)
+  tail call void @sink(ptr null), !dbg !11
+  ret void, !dbg !11
+}
+
+;; Here the address is materialized into a vreg, but describing the variable by
+;; that register would leave the start of the scope uncovered.
+; MIR-LABEL: name: global_stored
+; MIR: DBG_VALUE @g, $noreg, ![[#]], !DIExpression()
+; MIR-NOT: DBG_VALUE
+;
+; DWARF-LABEL: DW_AT_name ("global_stored")
+; DWARF: DW_TAG_variable
+; DWARF-NEXT: DW_AT_location (DW_OP_addrx 0x1, DW_OP_stack_value)
+; DWARF-NEXT: DW_AT_name ("y")
+define void @global_stored() !dbg !12 {
+entry:
+    #dbg_value(ptr @g, !13, !DIExpression(), !14)
+  %box = tail call ptr @alloc(), !dbg !14
+  store ptr @g, ptr %box, align 8, !dbg !14
+  tail call void @sink(ptr %box), !dbg !14
+  ret void, !dbg !14
+}
+
+;; A thread-local's address is not a link-time constant: describing it needs
+;; DW_OP_form_tls_address, so it must not be named as a symbol here. GlobalISel
+;; then drops it, which is what it did for every global before this -- naming the
+;; symbol does not regress that case, but does not fix it either.
+; MIR-LABEL: name: thread_local_global
+; MIR-NOT: DBG_VALUE @tls
+; MIR: DBG_VALUE $noreg, 0, ![[#]], !DIExpression()
+define void @thread_local_global() !dbg !15 {
+entry:
+    #dbg_value(ptr @tls, !16, !DIExpression(), !17)
+  tail call void @sink(ptr @tls), !dbg !17
+  ret void, !dbg !17
+}
+
+declare void @sink(ptr)
+declare ptr @alloc()
+
+!llvm.module.flags = !{!0, !1}
+!llvm.dbg.cu = !{!2}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = !{i32 7, !"Dwarf Version", i32 5}
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang", isOptimized: true, emissionKind: FullDebug)
+!3 = !DIFile(filename: "t.c", directory: "/")
+!4 = !DISubroutineType(types: !5)
+!5 = !{null}
+!6 = distinct !DISubprogram(name: "global_only", scope: !3, file: !3, line: 1, type: !4, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
+!7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64)
+!8 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed)
+!9 = distinct !DILexicalBlock(scope: !6, file: !3, line: 2, column: 1)
+!10 = !DILocalVariable(name: "x", scope: !9, file: !3, line: 2, type: !7)
+!11 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = distinct !DISubprogram(name: "global_stored", scope: !3, file: !3, line: 5, type: !4, scopeLine: 5, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
+!13 = !DILocalVariable(name: "y", scope: !18, file: !3, line: 6, type: !7)
+!14 = !DILocation(line: 6, column: 1, scope: !18)
+!15 = distinct !DISubprogram(name: "thread_local_global", scope: !3, file: !3, line: 10, type: !4, scopeLine: 10, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
+!16 = !DILocalVariable(name: "z", scope: !19, file: !3, line: 11, type: !7)
+!17 = !DILocation(line: 11, column: 1, scope: !19)
+!18 = distinct !DILexicalBlock(scope: !12, file: !3, line: 6, column: 1)
+!19 = distinct !DILexicalBlock(scope: !15, file: !3, line: 11, column: 1)
diff --git a/llvm/test/DebugInfo/X86/dbg-val-list-dangling.ll b/llvm/test/DebugInfo/X86/dbg-val-list-dangling.ll
index 6000b48911e36..f16177527cb1c 100644
--- a/llvm/test/DebugInfo/X86/dbg-val-list-dangling.ll
+++ b/llvm/test/DebugInfo/X86/dbg-val-list-dangling.ll
@@ -1,9 +1,5 @@
-;; At the moment we emit an undef as soon as we encounter "dangling" variadic
-;; dbg_value nodes. This does not reduce correctness but does reduce coverage.
-;; We should make variadic dbg_values work in the same way as their
-;; non-variadic counterparts here.
-;; FIXME: When dangling nodes for a variadic dbg_value are found, we should be
-;; able to recover the value in some cases.
+;; The address of a global is emitted as a location in its own right, so it never
+;; dangles and a variadic dbg_value recovers it just like a non-variadic one.
 
 ; RUN: llc %s -start-after=codegenprepare -stop-before=finalize-isel -o - -experimental-debug-variable-locations=false | FileCheck %s
 
@@ -22,8 +18,8 @@
 ; CHECK: ![[C:[0-9]+]] = !DILocalVariable(name: "c",
 ; CHECK: ![[D:[0-9]+]] = !DILocalVariable(name: "d",
 
-; CHECK-DAG: DBG_VALUE %[[VREG:[0-9]]], $noreg, ![[C]], !DIExpression(), debug-location
-; CHECK-DAG: DBG_VALUE_LIST ![[D]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value), $noreg, debug-location
+; CHECK-DAG: DBG_VALUE @.str, $noreg, ![[C]], !DIExpression(), debug-location
+; CHECK-DAG: DBG_VALUE_LIST ![[D]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value), @.str, debug-location
 
 target triple = "x86_64-unknown-linux-gnu"
 
diff --git a/llvm/test/DebugInfo/X86/dbg-value-globaladdr.ll b/llvm/test/DebugInfo/X86/dbg-value-globaladdr.ll
new file mode 100644
index 0000000000000..da7908ed6edcc
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/dbg-value-globaladdr.ll
@@ -0,0 +1,97 @@
+;; Check that a dbg_value naming the address of a global describes the variable
+;; directly, for the whole of its scope, rather than waiting for the address to
+;; be materialized into a register.
+
+; RUN: llc -O2 -mtriple=x86_64-unknown-linux-gnu -stop-after=finalize-isel < %s \
+; RUN:   | FileCheck %s --check-prefix=MIR
+;; The GlobalISel equivalent is DebugInfo/AArch64/dbg-value-globaladdr-gisel.ll.
+; RUN: llc -O2 -mtriple=x86_64-unknown-linux-gnu -filetype=obj < %s \
+; RUN:   | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF5
+; RUN: llc -O2 -mtriple=x86_64-unknown-linux-gnu -dwarf-version=4 -filetype=obj < %s \
+; RUN:   | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF4
+
+ at g = external global i64, align 8
+ at tls = external thread_local global i64, align 8
+
+;; Nothing in the function materializes the address, so before this was
+;; described the variable was dropped entirely.
+; MIR-LABEL: name: global_only
+; MIR: DBG_VALUE @g, $noreg, ![[#]], !DIExpression()
+;
+;; DW_OP_addrx indexes .debug_addr, which is relocated to @g.
+; DWARF5-LABEL: DW_AT_name ("global_only")
+; DWARF5: DW_TAG_variable
+; DWARF5-NEXT: DW_AT_location (DW_OP_addrx 0x1, DW_OP_stack_value)
+; DWARF5-NEXT: DW_AT_name ("x")
+;
+;; Before DWARF 5 there is no .debug_addr to index outside of split DWARF, so
+;; the address is spelled as a relocated DW_OP_addr instead.
+; DWARF4-LABEL: DW_AT_name ("global_only")
+; DWARF4: DW_TAG_variable
+; DWARF4-NEXT: DW_AT_location (DW_OP_addr 0x0, DW_OP_stack_value)
+; DWARF4-NEXT: DW_AT_name ("x")
+define void @global_only() !dbg !6 {
+entry:
+    #dbg_value(ptr @g, !10, !DIExpression(), !11)
+  tail call void @sink(ptr null), !dbg !11
+  ret void, !dbg !11
+}
+
+;; Here the address is materialized, but describing the variable by the
+;; resulting register would leave the start of the scope uncovered.
+; MIR-LABEL: name: global_stored
+; MIR: DBG_VALUE @g, $noreg, ![[#]], !DIExpression()
+; MIR-NOT: DBG_VALUE
+;
+; DWARF5-LABEL: DW_AT_name ("global_stored")
+; DWARF5: DW_TAG_variable
+; DWARF5-NEXT: DW_AT_location (DW_OP_addrx 0x1, DW_OP_stack_value)
+; DWARF5-NEXT: DW_AT_name ("y")
+define void @global_stored() !dbg !12 {
+entry:
+    #dbg_value(ptr @g, !13, !DIExpression(), !14)
+  %box = tail call ptr @alloc(), !dbg !14
+  store ptr @g, ptr %box, align 8, !dbg !14
+  tail call void @sink(ptr %box), !dbg !14
+  ret void, !dbg !14
+}
+
+;; A thread-local's address is not a link-time constant: describing it needs
+;; DW_OP_form_tls_address, so it must not be named as a symbol here.
+; MIR-LABEL: name: thread_local_global
+; MIR-NOT: DBG_VALUE @tls
+;; It still gets a register location recovered from the dangling debug info.
+; MIR: DBG_INSTR_REF ![[#]], !DIExpression(DW_OP_LLVM_arg, 0)
+define void @thread_local_global() !dbg !15 {
+entry:
+    #dbg_value(ptr @tls, !16, !DIExpression(), !17)
+  tail call void @sink(ptr @tls), !dbg !17
+  ret void, !dbg !17
+}
+
+declare void @sink(ptr)
+declare ptr @alloc()
+
+!llvm.module.flags = !{!0, !1}
+!llvm.dbg.cu = !{!2}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = !{i32 7, !"Dwarf Version", i32 5}
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang", isOptimized: true, emissionKind: FullDebug)
+!3 = !DIFile(filename: "t.c", directory: "/")
+!4 = !DISubroutineType(types: !5)
+!5 = !{null}
+!6 = distinct !DISubprogram(name: "global_only", scope: !3, file: !3, line: 1, type: !4, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
+!7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64)
+!8 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed)
+!10 = !DILocalVariable(name: "x", scope: !9, file: !3, line: 2, type: !7)
+!9 = distinct !DILexicalBlock(scope: !6, file: !3, line: 2, column: 1)
+!11 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = distinct !DISubprogram(name: "global_stored", scope: !3, file: !3, line: 5, type: !4, scopeLine: 5, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
+!13 = !DILocalVariable(name: "y", scope: !18, file: !3, line: 6, type: !7)
+!18 = distinct !DILexicalBlock(scope: !12, file: !3, line: 6, column: 1)
+!14 = !DILocation(line: 6, column: 1, scope: !18)
+!15 = distinct !DISubprogram(name: "thread_local_global", scope: !3, file: !3, line: 10, type: !4, scopeLine: 10, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
+!16 = !DILocalVariable(name: "z", scope: !19, file: !3, line: 11, type: !7)
+!19 = distinct !DILexicalBlock(scope: !15, file: !3, line: 11, column: 1)
+!17 = !DILocation(line: 11, column: 1, scope: !19)
diff --git a/llvm/test/DebugInfo/X86/sdag-dangling-dbgvalue.ll b/llvm/test/DebugInfo/X86/sdag-dangling-dbgvalue.ll
index 447546e0d210c..bd80af6a8cde6 100644
--- a/llvm/test/DebugInfo/X86/sdag-dangling-dbgvalue.ll
+++ b/llvm/test/DebugInfo/X86/sdag-dangling-dbgvalue.ll
@@ -1,9 +1,10 @@
 ; RUN: llc %s -stop-before finalize-isel -o - \
-; RUN:    -experimental-debug-variable-locations=false \
-; RUN: | FileCheck %s --check-prefixes=CHECK,DBGVALUE
+; RUN:    -experimental-debug-variable-locations=false | FileCheck %s
 ; RUN: llc %s -stop-before finalize-isel -o - \
-; RUN:    -experimental-debug-variable-locations=true \
-; RUN: | FileCheck %s --check-prefixes=CHECK,INSTRREF
+; RUN:    -experimental-debug-variable-locations=true | FileCheck %s
+
+;; The address of a global is emitted as a location in its own right, so none of
+;; these locations depend on an instruction and both modes agree.
 
 ;--------------------------------------------------------------------
 ; This test case is basically generated from the following C code.
@@ -67,59 +68,51 @@ target triple = "x86_64-apple-macosx10.4.0"
 
 @S = global %struct.SS { i32 23, i32 -17 }, align 4, !dbg !0
 
-; Verify that the def comes before the for foo1.
+; Verify that foo1 is described by the address of S rather than by the register
+; that address is later materialized into.
 define i32 @test1() local_unnamed_addr #0 !dbg !17 {
 ; CHECK-LABEL: bb.0.entry1
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[FOO1]], !DIExpression()
 ; CHECK-NEXT:    DBG_VALUE 0, $noreg, ![[BAR1]], !DIExpression()
-; CHECK-NEXT:    [[REG1:%[0-9]+]]:gr64 = LEA64r
-; INSTRREF-SAME:    debug-instr-number 1
-; INSTRREF-NEXT:  DBG_INSTR_REF ![[FOO1]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; DBGVALUE-NEXT:  DBG_VALUE [[REG1]], $noreg, ![[FOO1]], !DIExpression()
+; CHECK-NEXT:    %{{[0-9]+}}:gr64 = LEA64r
 entry1:
   call void @llvm.dbg.value(metadata ptr @S, metadata !20, metadata !DIExpression()), !dbg !23
   call void @llvm.dbg.value(metadata ptr null, metadata !22, metadata !DIExpression()), !dbg !24
   ret i32 ptrtoint (ptr @S to i32), !dbg !25
 }
 
-; Verify that the def comes before the for foo2 and bar2.
+; Verify that two variables sharing the address describe it independently.
 define i32 @test2() local_unnamed_addr #0 !dbg !26 {
 ; CHECK-LABEL: bb.0.entry2
-; CHECK-NEXT:    [[REG2:%[0-9]+]]:gr64 = LEA64r
-; INSTRREF-SAME:    debug-instr-number 1
-; INSTRREF-NEXT: DBG_INSTR_REF ![[FOO2]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; INSTRREF-NEXT: DBG_INSTR_REF ![[BAR2]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; DBGVALUE-NEXT: DBG_VALUE [[REG2]], $noreg, ![[FOO2]], !DIExpression
-; DBGVALUE-NEXT: DBG_VALUE [[REG2]], $noreg, ![[BAR2]], !DIExpression
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[FOO2]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[BAR2]], !DIExpression()
+; CHECK-NEXT:    %{{[0-9]+}}:gr64 = LEA64r
 entry2:
   call void @llvm.dbg.value(metadata ptr @S, metadata !28, metadata !DIExpression()), !dbg !30
   call void @llvm.dbg.value(metadata ptr @S, metadata !29, metadata !DIExpression()), !dbg !31
   ret i32 add (i32 ptrtoint (ptr @S to i32), i32 ptrtoint (ptr @S to i32)), !dbg !32
 }
 
-; Verify that the def comes before the for foo3 and bar3.
+; Verify the same in the opposite declaration order.
 define i32 @test3() local_unnamed_addr #0 !dbg !33 {
 ; CHECK-LABEL: bb.0.entry3
-; CHECK-NEXT:    [[REG3:%[0-9]+]]:gr64 = LEA64r
-; INSTRREF-SAME:    debug-instr-number 1
-; INSTRREF-NEXT: DBG_INSTR_REF ![[BAR3]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; INSTRREF-NEXT: DBG_INSTR_REF ![[FOO3]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; DBGVALUE-NEXT: DBG_VALUE [[REG3]], $noreg, ![[BAR3]], !DIExpression()
-; DBGVALUE-NEXT: DBG_VALUE [[REG3]], $noreg, ![[FOO3]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[BAR3]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[FOO3]], !DIExpression()
+; CHECK-NEXT:    %{{[0-9]+}}:gr64 = LEA64r
 entry3:
   call void @llvm.dbg.value(metadata ptr @S, metadata !36, metadata !DIExpression()), !dbg !38
   call void @llvm.dbg.value(metadata ptr @S, metadata !35, metadata !DIExpression()), !dbg !37
   ret i32 add (i32 ptrtoint (ptr @S to i32), i32 ptrtoint (ptr @S to i32)), !dbg !39
 }
 
-; Verify that the def comes before the for bar4.
+; Verify that a variable reassigned after taking the address is described in
+; program order, so that the later assignment wins.
 define i32 @test4() local_unnamed_addr #0 !dbg !40 {
 ; CHECK-LABEL: bb.0.entry4
-; CHECK-NEXT:    DBG_VALUE $noreg, $noreg, ![[FOO4]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[FOO4]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[BAR4]], !DIExpression()
 ; CHECK-NEXT:    DBG_VALUE 0, $noreg, ![[FOO4]], !DIExpression()
-; CHECK-NEXT:    [[REG4:%[0-9]+]]:gr64 = LEA64r
-; INSTRREF-SAME:    debug-instr-number 1
-; INSTRREF-NEXT: DBG_INSTR_REF ![[BAR4]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; DBGVALUE-NEXT: DBG_VALUE [[REG4]], $noreg, ![[BAR4]], !DIExpression()
+; CHECK-NEXT:    %{{[0-9]+}}:gr64 = LEA64r
 entry4:
   call void @llvm.dbg.value(metadata ptr @S, metadata !42, metadata !DIExpression()), !dbg !44
   call void @llvm.dbg.value(metadata ptr @S, metadata !43, metadata !DIExpression()), !dbg !45
@@ -127,15 +120,13 @@ entry4:
   ret i32 ptrtoint (ptr @S to i32), !dbg !46
 }
 
-; Verify that we do not get a DBG_VALUE that maps foo5 to @S here.
+; Verify that foo5 is not mapped back to @S after being reassigned.
 define i32 @test5() local_unnamed_addr #0 !dbg !47 {
 ; CHECK-LABEL: bb.0.entry5:
-; CHECK-NEXT:    DBG_VALUE $noreg, $noreg, ![[FOO5]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[BAR5]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[FOO5]], !DIExpression()
 ; CHECK-NEXT:    DBG_VALUE 0, $noreg, ![[FOO5]], !DIExpression()
-; CHECK-NEXT:    [[REG5:%[0-9]+]]:gr64 = LEA64r
-; INSTRREF-SAME:    debug-instr-number 1
-; INSTRREF-NEXT: DBG_INSTR_REF ![[BAR5]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; DBGVALUE-NEXT: DBG_VALUE [[REG5]], $noreg, ![[BAR5]], !DIExpression()
+; CHECK-NEXT:    %{{[0-9]+}}:gr64 = LEA64r
 ; CHECK-NOT:     DBG_{{.*}} ![[FOO5]], !DIExpression()
 ; CHECK:         RET
 entry5:
diff --git a/llvm/test/DebugInfo/assignment-tracking/X86/sdag-dangling-dbgassign.ll b/llvm/test/DebugInfo/assignment-tracking/X86/sdag-dangling-dbgassign.ll
index 347f001be1ca6..a8a173182d6a6 100644
--- a/llvm/test/DebugInfo/assignment-tracking/X86/sdag-dangling-dbgassign.ll
+++ b/llvm/test/DebugInfo/assignment-tracking/X86/sdag-dangling-dbgassign.ll
@@ -1,10 +1,11 @@
 ; RUN: llc %s -stop-before finalize-isel -o - \
-; RUN:    -experimental-debug-variable-locations=false \
-; RUN: | FileCheck %s --check-prefixes=CHECK,DBGVALUE
+; RUN:    -experimental-debug-variable-locations=false | FileCheck %s
 
 ; RUN: llc %s -stop-before finalize-isel -o - \
-; RUN:    -experimental-debug-variable-locations=true \
-; RUN: | FileCheck %s --check-prefixes=CHECK,INSTRREF
+; RUN:    -experimental-debug-variable-locations=true | FileCheck %s
+
+;; The address of a global is emitted as a location in its own right, so none of
+;; these locations depend on an instruction and both modes agree.
 
 ;--------------------------------------------------------------------
 ; Adapted from sdag-dangling-dbgvalue.ll to test dbg.assign intrinsics. This
@@ -72,60 +73,51 @@ target triple = "x86_64-apple-macosx10.4.0"
 
 @S = global %struct.SS { i32 23, i32 -17 }, align 4, !dbg !0
 
-; Verify that the def comes before the for foo1.
+; Verify that foo1 is described by the address of S rather than by the register
+; that address is later materialized into.
 define i32 @test1() local_unnamed_addr #0 !dbg !17 {
 ; CHECK-LABEL: bb.0.entry1
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[FOO1]], !DIExpression()
 ; CHECK-NEXT:    DBG_VALUE 0, $noreg, ![[BAR1]], !DIExpression()
-; CHECK-NEXT:    [[REG1:%[0-9]+]]:gr64 = LEA64r
-; INSTRREF-SAME:    debug-instr-number 1
-; INSTRREF-NEXT:  DBG_INSTR_REF ![[FOO1]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; DBGVALUE-NEXT:  DBG_VALUE [[REG1]], $noreg, ![[FOO1]], !DIExpression()
+; CHECK-NEXT:    %{{[0-9]+}}:gr64 = LEA64r
 entry1:
   call void @llvm.dbg.assign(metadata ptr @S, metadata !20, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !23
   call void @llvm.dbg.assign(metadata ptr null, metadata !22, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !24
   ret i32 ptrtoint (ptr @S to i32), !dbg !25
 }
 
-; Verify that the def comes before the for foo2 and bar2.
+; Verify that two variables sharing the address describe it independently.
 define i32 @test2() local_unnamed_addr #0 !dbg !26 {
 ; CHECK-LABEL: bb.0.entry2
-; CHECK-NEXT:    [[REG2:%[0-9]+]]:gr64 = LEA64r
-; INSTRREF-SAME:    debug-instr-number 1
-; INSTRREF-NEXT: DBG_INSTR_REF ![[FOO2]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; INSTRREF-NEXT: DBG_INSTR_REF ![[BAR2]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; DBGVALUE-NEXT: DBG_VALUE [[REG2]], $noreg, ![[FOO2]], !DIExpression
-; DBGVALUE-NEXT: DBG_VALUE [[REG2]], $noreg, ![[BAR2]], !DIExpression
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[FOO2]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[BAR2]], !DIExpression()
+; CHECK-NEXT:    %{{[0-9]+}}:gr64 = LEA64r
 entry2:
   call void @llvm.dbg.assign(metadata ptr @S, metadata !28, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !30
   call void @llvm.dbg.assign(metadata ptr @S, metadata !29, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !31
   ret i32 add (i32 ptrtoint (ptr @S to i32), i32 ptrtoint (ptr @S to i32)), !dbg !32
 }
 
-; Verify that the def comes before the for foo3 and bar3.
+; Verify the same in the opposite declaration order.
 define i32 @test3() local_unnamed_addr #0 !dbg !33 {
 ; CHECK-LABEL: bb.0.entry3
-; CHECK-NEXT:    [[REG3:%[0-9]+]]:gr64 = LEA64r
-; INSTRREF-SAME:    debug-instr-number 1
-; INSTRREF-NEXT: DBG_INSTR_REF ![[BAR3]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; INSTRREF-NEXT: DBG_INSTR_REF ![[FOO3]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; DBGVALUE-NEXT: DBG_VALUE [[REG3]], $noreg, ![[BAR3]], !DIExpression()
-; DBGVALUE-NEXT: DBG_VALUE [[REG3]], $noreg, ![[FOO3]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[BAR3]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[FOO3]], !DIExpression()
+; CHECK-NEXT:    %{{[0-9]+}}:gr64 = LEA64r
 entry3:
   call void @llvm.dbg.assign(metadata ptr @S, metadata !36, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !38
   call void @llvm.dbg.assign(metadata ptr @S, metadata !35, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !37
   ret i32 add (i32 ptrtoint (ptr @S to i32), i32 ptrtoint (ptr @S to i32)), !dbg !39
 }
 
-; Verify that the def comes before the for bar4.
+; Verify that a variable reassigned after taking the address is described in
+; program order, so that the later assignment wins. Assignment tracking has
+; already dropped foo4's redundant @S location.
 define i32 @test4() local_unnamed_addr #0 !dbg !40 {
 ; CHECK-LABEL: bb.0.entry4
-;; NOTE: The check for `DBG_VALUE $noreg, $noreg, ![[FOO4]], !DIExpression()`
-;; has been removed because AT lowering removes redundant debug intrinsics.
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[BAR4]], !DIExpression()
 ; CHECK-NEXT:    DBG_VALUE 0, $noreg, ![[FOO4]], !DIExpression()
-; CHECK-NEXT:    [[REG4:%[0-9]+]]:gr64 = LEA64r
-; INSTRREF-SAME:    debug-instr-number 1
-; INSTRREF-NEXT: DBG_INSTR_REF ![[BAR4]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; DBGVALUE-NEXT: DBG_VALUE [[REG4]], $noreg, ![[BAR4]], !DIExpression()
+; CHECK-NEXT:    %{{[0-9]+}}:gr64 = LEA64r
 entry4:
   call void @llvm.dbg.assign(metadata ptr @S, metadata !42, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !44
   call void @llvm.dbg.assign(metadata ptr @S, metadata !43, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !45
@@ -133,15 +125,12 @@ entry4:
   ret i32 ptrtoint (ptr @S to i32), !dbg !46
 }
 
-; Verify that we do not get a DBG_VALUE that maps foo5 to @S here.
+; Verify that foo5 is not mapped back to @S after being reassigned.
 define i32 @test5() local_unnamed_addr #0 !dbg !47 {
 ; CHECK-LABEL: bb.0.entry5:
-; cHECK-NEXT:    DBG_VALUE $noreg, $noreg, ![[FOO5]], !DIExpression()
+; CHECK-NEXT:    DBG_VALUE @S, $noreg, ![[BAR5]], !DIExpression()
 ; CHECK-NEXT:    DBG_VALUE 0, $noreg, ![[FOO5]], !DIExpression()
-; CHECK-NEXT:    [[REG5:%[0-9]+]]:gr64 = LEA64r
-; INSTRREF-SAME:    debug-instr-number 1
-; INSTRREF-NEXT: DBG_INSTR_REF ![[BAR5]], !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0)
-; DBGVALUE-NEXT: DBG_VALUE [[REG5]], $noreg, ![[BAR5]], !DIExpression()
+; CHECK-NEXT:    %{{[0-9]+}}:gr64 = LEA64r
 ; CHECK-NOT:     DBG_{{.*}} ![[FOO5]], !DIExpression()
 ; CHECK:         RET
 entry5:

>From ed798b33e20fad8912b302f6d7e184e8e55f2e71 Mon Sep 17 00:00:00 2001
From: Adrian Prantl <aprantl at apple.com>
Date: Tue, 25 Aug 2026 10:23:28 -0700
Subject: [PATCH 2/5] fixup! [DebugInfo] Support global addresses in variable
 locations

---
 llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
index 5c1b280697262..67848a25df227 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -1063,6 +1063,9 @@ LLVM_DUMP_METHOD void SDDbgValue::print(raw_ostream &OS) const {
     case SDDbgOperand::VREG:
       OS << "VREG=" << printReg(Op.getVReg());
       break;
+    case SDDbgOperand::GLOBALADDR:
+      OS << "GLOBALADDR=" << Op.getGlobal()->getName();
+      break;
     }
     Comma = true;
   }

>From d24bba9f2646876bf9a2354d7ceac493a7b78883 Mon Sep 17 00:00:00 2001
From: Adrian Prantl <aprantl at apple.com>
Date: Tue, 25 Aug 2026 13:21:24 -0700
Subject: [PATCH 3/5] fixup! [DebugInfo] Support global addresses in variable
 locations

---
 llvm/lib/CodeGen/Analysis.cpp                             | 3 +++
 llvm/test/DebugInfo/AArch64/dbg-value-globaladdr-gisel.ll | 4 ++--
 llvm/test/DebugInfo/X86/dbg-value-globaladdr.ll           | 4 ++--
 3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/CodeGen/Analysis.cpp b/llvm/lib/CodeGen/Analysis.cpp
index ff643b77678be..0579412a90613 100644
--- a/llvm/lib/CodeGen/Analysis.cpp
+++ b/llvm/lib/CodeGen/Analysis.cpp
@@ -530,6 +530,9 @@ static bool nextRealType(SmallVectorImpl<Type *> &SubTypes,
 }
 
 bool llvm::canDescribeGlobalAddressInDebugInfo(const GlobalValue *GV) {
+  // Only a definitions have an address a symbol reference can name.
+  if (GV->isDeclarationForLinker())
+    return false;
   // A thread-local's address is not known until it is resolved against a
   // thread's storage, which a plain symbol reference cannot express.
   if (GV->isThreadLocal())
diff --git a/llvm/test/DebugInfo/AArch64/dbg-value-globaladdr-gisel.ll b/llvm/test/DebugInfo/AArch64/dbg-value-globaladdr-gisel.ll
index 99f38390c8165..06b1ebe0848d7 100644
--- a/llvm/test/DebugInfo/AArch64/dbg-value-globaladdr-gisel.ll
+++ b/llvm/test/DebugInfo/AArch64/dbg-value-globaladdr-gisel.ll
@@ -9,8 +9,8 @@
 ; RUN: llc -O2 -mtriple=aarch64-apple-macosx -global-isel -filetype=obj < %s \
 ; RUN:   | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF
 
- at g = external global i64, align 8
- at tls = external thread_local global i64, align 8
+ at g = global i64 0, align 8
+ at tls = thread_local global i64 0, align 8
 
 ;; Nothing in the function materializes the address. GlobalISel has no dangling
 ;; debug info recovery at all, so before this the variable was dropped outright.
diff --git a/llvm/test/DebugInfo/X86/dbg-value-globaladdr.ll b/llvm/test/DebugInfo/X86/dbg-value-globaladdr.ll
index da7908ed6edcc..ccfdd109cba7f 100644
--- a/llvm/test/DebugInfo/X86/dbg-value-globaladdr.ll
+++ b/llvm/test/DebugInfo/X86/dbg-value-globaladdr.ll
@@ -10,8 +10,8 @@
 ; RUN: llc -O2 -mtriple=x86_64-unknown-linux-gnu -dwarf-version=4 -filetype=obj < %s \
 ; RUN:   | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF4
 
- at g = external global i64, align 8
- at tls = external thread_local global i64, align 8
+ at g = global i64 0, align 8
+ at tls = thread_local global i64 0, align 8
 
 ;; Nothing in the function materializes the address, so before this was
 ;; described the variable was dropped entirely.

>From c82a74fffa230b255d94bc5353ed64a52abb6669 Mon Sep 17 00:00:00 2001
From: Adrian Prantl <aprantl at apple.com>
Date: Wed, 9 Sep 2026 16:50:53 -0700
Subject: [PATCH 4/5] fixup! [DebugInfo] Support global addresses in variable
 locations

---
 llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h   |  27 +++-
 .../CodeGen/AsmPrinter/DwarfCompileUnit.cpp   |   6 +-
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp    |   6 +-
 .../CodeGen/AsmPrinter/DwarfExpression.cpp    |  13 +-
 llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h |   9 +-
 .../LiveDebugValues/VarLocBasedImpl.cpp       |  25 +++-
 .../DebugInfo/MIR/X86/globaladdr-offset.mir   | 115 ++++++++++++++++++
 7 files changed, 181 insertions(+), 20 deletions(-)
 create mode 100644 llvm/test/DebugInfo/MIR/X86/globaladdr-offset.mir

diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h
index 13621fb9ac9b9..ada7e9a7a9023 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h
@@ -35,6 +35,20 @@ struct TargetIndexLocation {
   }
 };
 
+/// This struct describes the address of a global, displaced by a constant.
+struct GlobalAddressLocation {
+  const GlobalValue *GV;
+  int64_t Offset;
+
+  GlobalAddressLocation() = default;
+  GlobalAddressLocation(const GlobalValue *GV, int64_t Offset)
+      : GV(GV), Offset(Offset) {}
+
+  bool operator==(const GlobalAddressLocation &Other) const {
+    return GV == Other.GV && Offset == Other.Offset;
+  }
+};
+
 /// A single location or constant within a variable location description, with
 /// either a single entry (with an optional DIExpression) used for a DBG_VALUE,
 /// or a list of entries used for a DBG_VALUE_LIST.
@@ -56,7 +70,6 @@ class DbgValueLocEntry {
     int64_t Int;
     const ConstantFP *CFP;
     const ConstantInt *CIP;
-    const GlobalValue *GV;
   } Constant;
 
   union {
@@ -64,6 +77,8 @@ class DbgValueLocEntry {
     MachineLocation Loc;
     /// Or a location from target specific location.
     TargetIndexLocation TIL;
+    /// Or the address of a global.
+    GlobalAddressLocation GAL;
   };
 
 public:
@@ -77,9 +92,8 @@ class DbgValueLocEntry {
   DbgValueLocEntry(MachineLocation Loc) : EntryKind(E_Location), Loc(Loc) {}
   DbgValueLocEntry(TargetIndexLocation Loc)
       : EntryKind(E_TargetIndexLocation), TIL(Loc) {}
-  DbgValueLocEntry(const GlobalValue *GV) : EntryKind(E_GlobalAddress) {
-    Constant.GV = GV;
-  }
+  DbgValueLocEntry(GlobalAddressLocation GAL)
+      : EntryKind(E_GlobalAddress), GAL(GAL) {}
 
   bool isLocation() const { return EntryKind == E_Location; }
   bool isIndirectLocation() const {
@@ -95,7 +109,8 @@ class DbgValueLocEntry {
   int64_t getInt() const { return Constant.Int; }
   const ConstantFP *getConstantFP() const { return Constant.CFP; }
   const ConstantInt *getConstantInt() const { return Constant.CIP; }
-  const GlobalValue *getGlobalAddress() const { return Constant.GV; }
+  const GlobalValue *getGlobalAddress() const { return GAL.GV; }
+  int64_t getGlobalOffset() const { return GAL.Offset; }
   MachineLocation getLoc() const { return Loc; }
   TargetIndexLocation getTargetIndexLocation() const { return TIL; }
   friend bool operator==(const DbgValueLocEntry &, const DbgValueLocEntry &);
@@ -275,7 +290,7 @@ inline bool operator==(const DbgValueLocEntry &A, const DbgValueLocEntry &B) {
   case DbgValueLocEntry::E_ConstantInt:
     return A.Constant.CIP == B.Constant.CIP;
   case DbgValueLocEntry::E_GlobalAddress:
-    return A.Constant.GV == B.Constant.GV;
+    return A.GAL == B.GAL;
   }
   llvm_unreachable("unhandled EntryKind");
 }
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index cf395846a59d0..3b708cc111d49 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -928,7 +928,8 @@ void DwarfCompileUnit::applyConcreteDbgVariableAttributes(
       DIELoc *Loc = new (DIEValueAllocator) DIELoc;
       DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);
       DwarfExpr.addFragmentOffset(Expr);
-      if (!DwarfExpr.addGlobalAddress(Entry->getGlobalAddress()))
+      if (!DwarfExpr.addGlobalAddress(Entry->getGlobalAddress(),
+                                      Entry->getGlobalOffset()))
         return;
       DwarfExpr.addExpression(Expr);
       addBlock(VariableDie, dwarf::DW_AT_location, DwarfExpr.finalize());
@@ -986,7 +987,8 @@ void DwarfCompileUnit::applyConcreteDbgVariableAttributes(
       assert(Asm->TM.getTargetTriple().isWasm());
       DwarfExpr.addWasmLocation(Loc.Index, static_cast<uint64_t>(Loc.Offset));
     } else if (Entry.isGlobalAddress()) {
-      if (!DwarfExpr.addGlobalAddress(Entry.getGlobalAddress()))
+      if (!DwarfExpr.addGlobalAddress(Entry.getGlobalAddress(),
+                                      Entry.getGlobalOffset()))
         return false;
     } else {
       llvm_unreachable("Unsupported Entry type.");
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 14a872af897e7..146032c676be9 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -259,7 +259,8 @@ static DbgValueLoc getDebugLocValue(const MachineInstr *MI) {
       DbgValueLocEntries.push_back(
           DbgValueLocEntry(TargetIndexLocation(Op.getIndex(), Op.getOffset())));
     } else if (Op.isGlobal()) {
-      DbgValueLocEntries.push_back(DbgValueLocEntry(Op.getGlobal()));
+      DbgValueLocEntries.push_back(DbgValueLocEntry(
+          GlobalAddressLocation(Op.getGlobal(), Op.getOffset())));
     } else if (Op.isImm())
       DbgValueLocEntries.push_back(DbgValueLocEntry(Op.getImm()));
     else if (Op.isFPImm())
@@ -3347,7 +3348,8 @@ void DwarfDebug::emitDebugLocValue(const AsmPrinter &AP, const DIBasicType *BT,
       assert(AP.TM.getTargetTriple().isWasm());
       DwarfExpr.addWasmLocation(Loc.Index, static_cast<uint64_t>(Loc.Offset));
     } else if (Entry.isGlobalAddress()) {
-      if (!DwarfExpr.addGlobalAddress(Entry.getGlobalAddress()))
+      if (!DwarfExpr.addGlobalAddress(Entry.getGlobalAddress(),
+                                      Entry.getGlobalOffset()))
         return false;
     } else if (Entry.isConstantFP()) {
       if (AP.getDwarfVersion() >= 4 && !AP.getDwarfDebug()->tuneForSCE() &&
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
index d1952555ef76f..7a37813f04e85 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
@@ -862,7 +862,7 @@ void DwarfExpression::emitLegacyZExt(unsigned FromBits) {
   emitOp(dwarf::DW_OP_and);
 }
 
-bool DwarfExpression::addGlobalAddress(const GlobalValue *GV) {
+bool DwarfExpression::addGlobalAddress(const GlobalValue *GV, int64_t Offset) {
   DwarfDebug &DD = CU.getDwarfDebug();
 
   // Prefer the address pool, whose index is plain data and so can be emitted
@@ -885,6 +885,17 @@ bool DwarfExpression::addGlobalAddress(const GlobalValue *GV) {
     emitOp(dwarf::DW_OP_addr);
     emitRelocatedAddress(Sym);
   }
+
+  // The displacement cannot be folded into the address itself: a pool entry is
+  // keyed on the symbol alone, and a DW_FORM_addr label carries no addend. Let
+  // the expression apply it instead.
+  if (Offset > 0) {
+    emitOp(dwarf::DW_OP_plus_uconst);
+    emitUnsigned(Offset);
+  } else if (Offset < 0) {
+    addSignedConstant(Offset);
+    emitOp(dwarf::DW_OP_plus);
+  }
   return true;
 }
 
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
index 60dc8f0cadaf1..146e240d6592f 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
@@ -320,10 +320,11 @@ class DwarfExpression {
   /// The Index is an identifier for locals, globals or operand stack.
   void addWasmLocation(unsigned Index, uint64_t Offset);
 
-  /// Emit the address of \p GV as an implicit location description, i.e. as the
-  /// value of the described entity rather than as the address of its storage.
-  /// Returns false if the address cannot be spelled in this unit's DWARF.
-  bool addGlobalAddress(const GlobalValue *GV);
+  /// Emit the address of \p GV displaced by \p Offset as an implicit location
+  /// description, i.e. as the value of the described entity rather than as the
+  /// address of its storage. Returns false if the address cannot be spelled in
+  /// this unit's DWARF.
+  bool addGlobalAddress(const GlobalValue *GV, int64_t Offset);
 };
 
 /// DwarfExpression implementation for .debug_loc entries.
diff --git a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp
index 7cc0b117057e6..84b4f64dd510f 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp
@@ -326,6 +326,14 @@ class VarLocBasedLDV : public LDVImpl {
       bool operator!=(const WasmLoc &Other) const { return !(*this == Other); }
     };
 
+    struct GlobalAddr {
+      const GlobalValue *GV;
+      int64_t Offset;
+      bool operator==(const GlobalAddr &Other) const {
+        return GV == Other.GV && Offset == Other.Offset;
+      }
+    };
+
     /// Identity of the variable at this location.
     const DebugVariable Var;
 
@@ -362,7 +370,7 @@ class VarLocBasedLDV : public LDVImpl {
       const ConstantFP *FPImm;
       const ConstantInt *CImm;
       WasmLoc WasmLocation;
-      const GlobalValue *GV;
+      GlobalAddr GlobalAddress;
       MachineLocValue() : Hash(0) {}
     };
 
@@ -381,9 +389,10 @@ class VarLocBasedLDV : public LDVImpl {
           return Value.SpillLocation == Other.Value.SpillLocation;
         case MachineLocKind::WasmLocKind:
           return Value.WasmLocation == Other.Value.WasmLocation;
+        case MachineLocKind::GlobalAddrKind:
+          return Value.GlobalAddress == Other.Value.GlobalAddress;
         case MachineLocKind::RegisterKind:
         case MachineLocKind::ImmediateKind:
-        case MachineLocKind::GlobalAddrKind:
           return Value.Hash == Other.Value.Hash;
         default:
           llvm_unreachable("Invalid kind");
@@ -405,9 +414,13 @@ class VarLocBasedLDV : public LDVImpl {
                                  Value.WasmLocation.Offset) <
                  std::make_tuple(Other.Kind, Other.Value.WasmLocation.Index,
                                  Other.Value.WasmLocation.Offset);
+        case MachineLocKind::GlobalAddrKind:
+          return std::make_tuple(Kind, Value.GlobalAddress.GV,
+                                 Value.GlobalAddress.Offset) <
+                 std::make_tuple(Other.Kind, Other.Value.GlobalAddress.GV,
+                                 Other.Value.GlobalAddress.Offset);
         case MachineLocKind::RegisterKind:
         case MachineLocKind::ImmediateKind:
-        case MachineLocKind::GlobalAddrKind:
           return std::tie(Kind, Value.Hash) <
                  std::tie(Other.Kind, Other.Value.Hash);
         default:
@@ -474,7 +487,7 @@ class VarLocBasedLDV : public LDVImpl {
         Loc.WasmLocation = {Op.getIndex(), Op.getOffset()};
       } else if (Op.isGlobal()) {
         Kind = MachineLocKind::GlobalAddrKind;
-        Loc.GV = Op.getGlobal();
+        Loc.GlobalAddress = {Op.getGlobal(), Op.getOffset()};
       } else
         llvm_unreachable("Invalid Op kind for MachineLoc.");
       return {Kind, Loc};
@@ -743,7 +756,9 @@ class VarLocBasedLDV : public LDVImpl {
           Out << MLoc.Value.Immediate;
           break;
         case MachineLocKind::GlobalAddrKind:
-          Out << MLoc.Value.GV->getName();
+          Out << MLoc.Value.GlobalAddress.GV->getName();
+          if (MLoc.Value.GlobalAddress.Offset)
+            Out << '+' << MLoc.Value.GlobalAddress.Offset;
           break;
         case MachineLocKind::WasmLocKind: {
           if (TII) {
diff --git a/llvm/test/DebugInfo/MIR/X86/globaladdr-offset.mir b/llvm/test/DebugInfo/MIR/X86/globaladdr-offset.mir
new file mode 100644
index 0000000000000..3fb9f8428e5de
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/X86/globaladdr-offset.mir
@@ -0,0 +1,115 @@
+# RUN: llc -mtriple=x86_64-unknown-linux-gnu -dwarf-version=5 \
+# RUN:   -filetype=obj -start-after=livedebugvalues -o - %s \
+# RUN:   | llvm-dwarfdump - | FileCheck %s
+
+# A displacement cannot be folded into an address pool entry, which is keyed on
+# the symbol alone, so it has to be applied by the expression.
+
+# CHECK-LABEL: DW_AT_name ("globaladdr_offset")
+# CHECK:       DW_TAG_variable
+# CHECK:       DW_AT_location (DW_OP_addrx {{0x[0-9a-f]+}}, DW_OP_plus_uconst 0x8, DW_OP_stack_value)
+# CHECK:       DW_AT_name ("x")
+
+# CHECK-LABEL: DW_AT_name ("globaladdr_negative_offset")
+# CHECK:       DW_TAG_variable
+# CHECK:       DW_AT_location (DW_OP_addrx {{0x[0-9a-f]+}}, DW_OP_consts -8, DW_OP_plus, DW_OP_stack_value)
+# CHECK:       DW_AT_name ("y")
+
+# Two locations that differ only in their displacement must not be merged into
+# a single range.
+
+# CHECK-LABEL: DW_AT_name ("globaladdr_offset_loclist")
+# CHECK:       DW_TAG_variable
+# CHECK:       DW_AT_location ({{.*}}loclist
+# CHECK-NEXT:  DW_OP_addrx [[POOL:0x[0-9a-f]+]], DW_OP_stack_value
+# CHECK-NEXT:  DW_OP_addrx [[POOL]], DW_OP_plus_uconst 0x8, DW_OP_stack_value
+# CHECK:       DW_AT_name ("z")
+--- |
+  ; ModuleID = 'globaladdr-offset.c'
+  source_filename = "globaladdr-offset.c"
+  target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+  target triple = "x86_64-unknown-linux-gnu"
+
+  @g = global i64 0, align 8
+
+  define void @globaladdr_offset() !dbg !5 {
+  entry:
+    ret void, !dbg !10
+  }
+
+  define void @globaladdr_negative_offset() !dbg !12 {
+  entry:
+    ret void, !dbg !14
+  }
+
+  define void @globaladdr_offset_loclist(i1 %c) !dbg !15 {
+  entry:
+    br i1 %c, label %taken, label %exit
+  taken:
+    br label %exit
+  exit:
+    ret void, !dbg !17
+  }
+
+  !llvm.module.flags = !{!0, !1}
+  !llvm.dbg.cu = !{!2}
+
+  !0 = !{i32 2, !"Debug Info Version", i32 3}
+  !1 = !{i32 7, !"Dwarf Version", i32 5}
+  !2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "oracle", isOptimized: true, emissionKind: FullDebug)
+  !3 = !DIFile(filename: "globaladdr-offset.c", directory: "/")
+  !4 = !DISubroutineType(types: !11)
+  !5 = distinct !DISubprogram(name: "globaladdr_offset", scope: !3, file: !3, line: 1, type: !4, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !6)
+  !6 = !{!7}
+  !7 = !DILocalVariable(name: "x", scope: !5, file: !3, line: 2, type: !8)
+  !8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64)
+  !9 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed)
+  !10 = !DILocation(line: 2, column: 1, scope: !5)
+  !11 = !{null}
+  !12 = distinct !DISubprogram(name: "globaladdr_negative_offset", scope: !3, file: !3, line: 4, type: !4, scopeLine: 4, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !18)
+  !13 = !DILocalVariable(name: "y", scope: !12, file: !3, line: 5, type: !8)
+  !14 = !DILocation(line: 5, column: 1, scope: !12)
+  !15 = distinct !DISubprogram(name: "globaladdr_offset_loclist", scope: !3, file: !3, line: 7, type: !4, scopeLine: 7, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !19)
+  !16 = !DILocalVariable(name: "z", scope: !15, file: !3, line: 8, type: !8)
+  !17 = !DILocation(line: 8, column: 1, scope: !15)
+  !18 = !{!13}
+  !19 = !{!16}
+...
+---
+name:            globaladdr_offset
+alignment:       16
+body:             |
+  bb.0.entry:
+    DBG_VALUE @g + 8, $noreg, !7, !DIExpression(), debug-location !10
+    RET64 debug-location !10
+...
+---
+name:            globaladdr_negative_offset
+alignment:       16
+body:             |
+  bb.0.entry:
+    DBG_VALUE @g + -8, $noreg, !13, !DIExpression(), debug-location !14
+    RET64 debug-location !14
+...
+---
+name:            globaladdr_offset_loclist
+alignment:       16
+tracksRegLiveness: true
+body:             |
+  bb.0.entry:
+    successors: %bb.1, %bb.2
+    liveins: $edi
+
+    DBG_VALUE @g + 0, $noreg, !16, !DIExpression(), debug-location !17
+    TEST8ri killed renamable $dil, 1, implicit-def $eflags
+    JCC_1 %bb.2, 4, implicit $eflags
+
+  bb.1.taken:
+    successors: %bb.2
+
+    DBG_VALUE @g + 8, $noreg, !16, !DIExpression(), debug-location !17
+    $eax = MOV32ri 0, debug-location !17
+
+  bb.2.exit:
+    RET64 debug-location !17
+...

>From 48d4f0e1d4f5b952c062916396503e2af8a62297 Mon Sep 17 00:00:00 2001
From: Adrian Prantl <aprantl at apple.com>
Date: Wed, 9 Sep 2026 17:05:10 -0700
Subject: [PATCH 5/5] fixup! [DebugInfo] Support global addresses in variable
 locations

---
 .../CodeGen/AsmPrinter/DwarfCompileUnit.cpp   |  3 ++
 .../DebugInfo/X86/globaladdr-tag-offset.ll    | 36 +++++++++++++++++++
 2 files changed, 39 insertions(+)
 create mode 100644 llvm/test/DebugInfo/X86/globaladdr-tag-offset.ll

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 3b708cc111d49..94ee63d75e155 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -933,6 +933,9 @@ void DwarfCompileUnit::applyConcreteDbgVariableAttributes(
         return;
       DwarfExpr.addExpression(Expr);
       addBlock(VariableDie, dwarf::DW_AT_location, DwarfExpr.finalize());
+      if (DwarfExpr.TagOffset)
+        addUInt(VariableDie, dwarf::DW_AT_LLVM_tag_offset, dwarf::DW_FORM_data1,
+                *DwarfExpr.TagOffset);
     } else if (Entry->isTargetIndexLocation()) {
       DIELoc *Loc = new (DIEValueAllocator) DIELoc;
       DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);
diff --git a/llvm/test/DebugInfo/X86/globaladdr-tag-offset.ll b/llvm/test/DebugInfo/X86/globaladdr-tag-offset.ll
new file mode 100644
index 0000000000000..7ddffae16baa8
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/globaladdr-tag-offset.ll
@@ -0,0 +1,36 @@
+; RUN: llc -O2 -mtriple=x86_64-unknown-linux-gnu -filetype=obj < %s \
+; RUN:   | llvm-dwarfdump - | FileCheck %s
+
+ at g = global i64 0, align 8
+
+; CHECK-LABEL: DW_AT_name ("globaladdr_tag_offset")
+; CHECK:       DW_TAG_variable
+; CHECK:       DW_AT_location (DW_OP_addrx {{0x[0-9a-f]+}}, DW_OP_stack_value)
+; CHECK:       DW_AT_LLVM_tag_offset (0x07)
+; CHECK:       DW_AT_name ("x")
+define void @globaladdr_tag_offset() !dbg !6 {
+entry:
+  call void @llvm.dbg.value(metadata ptr @g, metadata !10, metadata !DIExpression(DW_OP_LLVM_tag_offset, 7)), !dbg !11
+  tail call void @sink(ptr null), !dbg !11
+  ret void, !dbg !11
+}
+
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+declare void @sink(ptr)
+
+!llvm.module.flags = !{!0, !1}
+!llvm.dbg.cu = !{!2}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = !{i32 7, !"Dwarf Version", i32 5}
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang", isOptimized: true, emissionKind: FullDebug)
+!3 = !DIFile(filename: "globaladdr-tag-offset.c", directory: "/")
+!4 = !DISubroutineType(types: !5)
+!5 = !{null}
+!6 = distinct !DISubprogram(name: "globaladdr_tag_offset", scope: !3, file: !3, line: 1, type: !4, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !12)
+!7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64)
+!8 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed)
+!10 = !DILocalVariable(name: "x", scope: !9, file: !3, line: 2, type: !7)
+!9 = distinct !DILexicalBlock(scope: !6, file: !3, line: 2, column: 1)
+!11 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = !{!10}



More information about the llvm-commits mailing list