[llvm-branch-commits] [llvm] [NFC][SPIRV] Converge different `resolveDebugParentScope` versions into a single one (PR #219925)

Juan Manuel Martinez CaamaƱo via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Sep 1 02:15:15 PDT 2026


https://github.com/jmmartinez updated https://github.com/llvm/llvm-project/pull/219925

>From 7d906ae5a2c3dcb05d152e1e66f48da8f275b42e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Juan=20Manuel=20Martinez=20Caama=C3=B1o?=
 <jmartinezcaamao at gmail.com>
Date: Mon, 31 Aug 2026 11:21:08 +0200
Subject: [PATCH 1/2] [NFC][SPIRV] Converge different resolveDebugParentScope
 versions into a single one

---
 .../SPIRV/SPIRVNonSemanticDebugHandler.cpp    | 111 ++++--------------
 .../SPIRV/SPIRVNonSemanticDebugHandler.h      |  47 ++------
 2 files changed, 36 insertions(+), 122 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 96862cad99afa..26c071fe2e18d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -651,59 +651,24 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
 }
 
 // Match SPIRV-LLVM-Translator's selection logic for the Parent operand.
-std::optional<MCRegister>
-SPIRVNonSemanticDebugHandler::resolveDebugFunctionParent(
-    const DISubprogram *SP) const {
-  const DIScope *Scope = SP->getScope();
-  if (Scope && !isa<DIFile>(Scope)) {
-    // Find the DINamespace that was emitted as a lexical block.
-    if (isa<DINamespace>(Scope))
-      return lookupOptReg(DebugLexicalBlockRegs, Scope);
-    // TODO: Complete with other lookups once other scopes are supported
-    // (subclasses of DIScope).
-    const DIType *Ty = dyn_cast<DIType>(Scope);
-    if (!Ty)
-      return std::nullopt;
-    return lookupOptReg(DebugTypeRegs, Ty);
-  }
-
-  const DICompileUnit *ParentCU = SP->getUnit();
-  if (!ParentCU && !CompileUnits.empty())
-    ParentCU = CompileUnits[0].TheCU;
-  if (!ParentCU)
-    return std::nullopt;
-  return lookupOptReg(CUToCompilationUnitDbgReg, ParentCU);
-}
-
-std::optional<MCRegister> SPIRVNonSemanticDebugHandler::resolveTypeScopeParent(
-    const DIScope *Scope) const {
-  // When the scope is itself a type (e.g. a struct nested in another struct),
-  // the parent is that enclosing type's debug id.
+std::optional<MCRegister> SPIRVNonSemanticDebugHandler::resolveScope(
+    const DIScope *Scope, const DICompileUnit *FallbackCU) const {
   if (const auto *Ty = dyn_cast_or_null<DIType>(Scope))
     return lookupOptReg(DebugTypeRegs, Ty);
 
-  // Find the DINamespace that was emitted as a lexical block.
-  if (isa_and_nonnull<DINamespace>(Scope))
+  if (isa_and_nonnull<DILexicalBlock, DINamespace>(Scope))
     return lookupOptReg(DebugLexicalBlockRegs, Scope);
 
-  // For a file, compile-unit, or absent scope, the parent is the first module
-  // DebugCompilationUnit.
-  if (CompileUnits.empty())
-    return std::nullopt;
-
-  return lookupOptReg(CUToCompilationUnitDbgReg, CompileUnits[0].TheCU);
-}
-
-std::optional<MCRegister>
-SPIRVNonSemanticDebugHandler::resolveLexicalBlockParent(
-    const DIScope *Scope) const {
-  if (isa_and_nonnull<DILexicalBlock>(Scope) ||
-      isa_and_nonnull<DINamespace>(Scope))
-    return lookupOptReg(DebugLexicalBlockRegs, Scope);
   if (const auto *SP = dyn_cast_or_null<DISubprogram>(Scope))
     return lookupOptReg(DebugFunctionRegs, SP);
+
+  // For a file, compile-unit, or absent scope, fall back to a compile unit.
+  if (FallbackCU)
+    return lookupOptReg(CUToCompilationUnitDbgReg, FallbackCU);
+
   if (CompileUnits.empty())
     return std::nullopt;
+
   return lookupOptReg(CUToCompilationUnitDbgReg, CompileUnits[0].TheCU);
 }
 
@@ -712,7 +677,7 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugLexicalBlock(
     MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
   assert((isa<DILexicalBlock, DINamespace>(S)) &&
          "S must be a DILexicalBlock or DINamespace in emitDebugLexicalBlock");
-  auto ParentRegOpt = resolveLexicalBlockParent(S->getScope());
+  auto ParentRegOpt = resolveScope(S->getScope());
   if (!ParentRegOpt)
     return std::nullopt;
 
@@ -757,7 +722,7 @@ SPIRVNonSemanticDebugHandler::emitDebugFunctionDeclaration(
     return std::nullopt;
   MCRegister FnTyReg = *FnTyRegOpt;
 
-  auto ParentRegOpt = resolveDebugFunctionParent(SP);
+  auto ParentRegOpt = resolveScope(SP->getScope(), SP->getUnit());
   if (!ParentRegOpt)
     return std::nullopt;
 
@@ -798,7 +763,7 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugFunction(
   if (!FnTyRegOpt)
     return std::nullopt;
 
-  auto ParentRegOpt = resolveDebugFunctionParent(SP);
+  auto ParentRegOpt = resolveScope(SP->getScope(), SP->getUnit());
   if (!ParentRegOpt)
     return std::nullopt;
 
@@ -842,30 +807,6 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
   return lookupOptReg(DebugTypeRegs, Ty);
 }
 
-MCRegister SPIRVNonSemanticDebugHandler::resolveGlobalVariableParent(
-    const DIGlobalVariable *GV) const {
-  // A namespace-scoped global variable's parent is the enclosing
-  // DebugLexicalBlock emitted for that DINamespace.
-  // TODO: When this backend emits debug instructions for subprogram,
-  // compilation units, and module scopes, also return GV->getScope()'s debug
-  // id for those cases.
-  if (isa_and_nonnull<DINamespace>(GV->getScope())) {
-    if (auto ParentRegOpt = lookupOptReg(DebugLexicalBlockRegs, GV->getScope()))
-      return *ParentRegOpt;
-  }
-
-  // !CompileUnits.empty() was already checked before staring the emission of
-  // NSDI instructions.
-  assert(!CompileUnits.empty() &&
-         "resolveGlobalVariableParent requires non-empty CompileUnits");
-  std::optional<MCRegister> ParentRegOpt =
-      lookupOptReg(CUToCompilationUnitDbgReg, CompileUnits[0].TheCU);
-  assert(ParentRegOpt && "DebugCompilationUnit must be emitted before "
-                         "resolveGlobalVariableParent");
-  // Fallback: first module compile unit (SPIRV-LLVM-Translator default).
-  return *ParentRegOpt;
-}
-
 // Unimplemented no-op; see emitDebugExpression declaration.
 std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugExpression(
     const DIExpression *, MCRegister, MCRegister, SPIRV::ModuleAnalysisInfo &) {
@@ -878,7 +819,12 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugGlobalVariable(
     SPIRV::ModuleAnalysisInfo &MAI) {
   assert(GV && "GV must not be null in emitDebugGlobalVariable");
 
-  MCRegister ParentReg = resolveGlobalVariableParent(GV);
+  assert(!CompileUnits.empty() &&
+         "emitDebugGlobalVariable requires non-empty CompileUnits");
+  auto ParentRegOpt = resolveScope(GV->getScope());
+  assert(ParentRegOpt && "DebugCompilationUnit must be emitted before "
+                         "emitDebugGlobalVariable");
+  MCRegister ParentReg = *ParentRegOpt;
 
   // TyReg: DebugInfoNone when GV has no DI type (as done in
   // SPIRV-LLVM-Translator). Declarations (isDefinition: false) can have null
@@ -1051,7 +997,7 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypeComposite(
     const DICompositeType *CT, ArrayRef<MCRegister> MemberRegs,
     MCRegister VoidTypeReg, MCRegister I32TypeReg, MCRegister ExtInstSetReg,
     SPIRV::ModuleAnalysisInfo &MAI) {
-  auto ParentRegOpt = resolveTypeScopeParent(CT->getScope());
+  auto ParentRegOpt = resolveScope(CT->getScope());
   if (!ParentRegOpt)
     return std::nullopt;
 
@@ -1105,21 +1051,10 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypedef(
   // Parent must be a lexical scope. Valid NSDI lexical scopes are
   // DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or
   // DebugTypeComposite.
-  //
-  // FIXME: We currently only emit DebugCompilationUnit, so the compile unit is
-  // the only parent available today.
-  MCRegister ParentReg;
-  if (const auto *Ty = dyn_cast_or_null<DIType>(TD->getScope()))
-    if (auto TyRegOpt = lookupOptReg(DebugTypeRegs, Ty))
-      ParentReg = *TyRegOpt;
-  if (!ParentReg.isValid()) {
-    assert(!CompileUnits.empty() &&
-           "emitDebugTypedef requires a compile unit for the Parent operand");
-    auto CURegOpt =
-        lookupOptReg(CUToCompilationUnitDbgReg, CompileUnits[0].TheCU);
-    assert(CURegOpt && "DebugCompilationUnit must be emitted before typedefs");
-    ParentReg = *CURegOpt;
-  }
+  auto ParentRegOpt = resolveScope(TD->getScope());
+  if (!ParentRegOpt)
+    return std::nullopt;
+  MCRegister ParentReg = *ParentRegOpt;
 
   return emitExtInst(
       SPIRV::NonSemanticExtInst::DebugTypedef, VoidTypeReg, ExtInstSetReg,
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 0b3c6fa0aa081..04faf612df920 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -334,8 +334,7 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// DISubroutineType type, the signature type was not emitted in \c
   /// DebugTypeRegs, no path
   /// \c OpString was recorded for \p SP in section 7, or
-  /// \c resolveDebugFunctionParent returns no id for the \c Parent
-  /// operand.
+  /// \c resolveScope returns no id for the \c Parent operand.
   std::optional<MCRegister>
   emitDebugFunctionDeclaration(const DISubprogram *SP, MCRegister VoidTypeReg,
                                MCRegister I32TypeReg, MCRegister ExtInstSetReg,
@@ -371,9 +370,6 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
       MCRegister VoidTypeReg, MCRegister I32TypeReg, MCRegister ExtInstSetReg,
       SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Resolve the \c Parent operand for \c DebugGlobalVariable.
-  MCRegister resolveGlobalVariableParent(const DIGlobalVariable *GV) const;
-
   /// Emit \c DebugExpression for \p Expr. Unimplemented: defined as a no-op
   /// (\returns \c std::nullopt, emits nothing) so \c emitDebugGlobalVariable
   /// can complete Variable-operand resolution for the opcodes we support today.
@@ -480,34 +476,18 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
                                                MCRegister ExtInstSetReg,
                                                SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Resolve the \c Parent operand for \c DebugFunctionDeclaration and
-  /// \c DebugFunction: an emitted debug type id when \c SP->getScope() is a
-  /// \c DIType in \c DebugTypeRegs, otherwise \c DebugCompilationUnit for
-  /// \c SP->getUnit() (or the first module CU when \c unit: is absent).
-  /// \returns \c std::nullopt when the scope requires a parent we cannot supply
-  /// (non-file scope that is not a mapped \c DIType) or the CU has no emitted
-  /// id.
-  std::optional<MCRegister>
-  resolveDebugFunctionParent(const DISubprogram *SP) const;
-
-  /// Resolve the \c Parent operand for a type instruction (\c
-  /// DebugTypeComposite) from its \p Scope: an emitted debug type id when \p
-  /// Scope is a \c DIType in \c DebugTypeRegs (a type nested in another type),
-  /// otherwise the first module \c DebugCompilationUnit.
-  /// \returns \c std::nullopt when \p Scope is a \c DIType that has not been
-  /// emitted, or when there is no compile unit.
-  std::optional<MCRegister> resolveTypeScopeParent(const DIScope *Scope) const;
-
-  /// Resolve the \c Parent operand for \c DebugLexicalBlock, and for any other
-  /// instruction whose LLVM scope may be a \c DILexicalBlock or \c
-  /// DINamespace: an emitted \c DebugLexicalBlock id when \p Scope is a \c
-  /// DILexicalBlock or \c DINamespace already in \c DebugLexicalBlockRegs, an
-  /// emitted \c DebugFunction id when \p Scope is a defining \c DISubprogram,
-  /// otherwise the first module \c DebugCompilationUnit.
-  /// \returns \c std::nullopt when \p Scope requires a parent we cannot
-  /// supply, or the fallback CU has no emitted id.
+  /// Map \p Scope to the NonSemantic debug id used as a \c Parent operand.
+  ///
+  /// Checks \c DebugTypeRegs, \c DebugLexicalBlockRegs, and \c
+  /// DebugFunctionRegs in order. When \p Scope is null, a \c DIFile, or
+  /// another scope without a dedicated debug instruction, falls back to \p
+  /// FallbackCU or the first module \c DebugCompilationUnit.
+  ///
+  /// \returns \c std::nullopt when \p Scope names an emitted scope that has
+  /// not been recorded yet, or when no fallback compile unit is available.
   std::optional<MCRegister>
-  resolveLexicalBlockParent(const DIScope *Scope) const;
+  resolveScope(const DIScope *Scope,
+               const DICompileUnit *FallbackCU = nullptr) const;
 
   /// Emit \c DebugLexicalBlock for \p S, which must be a \c DILexicalBlock or
   /// a \c DINamespace. A \c DILexicalBlock supplies Line/Column
@@ -515,8 +495,7 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// emitted as 0, and its Name is appended as an extra \c OpString operand.
   ///
   /// \returns The result id register on success. Returns \c std::nullopt and
-  /// emits nothing if \c resolveLexicalBlockParent returns no id for
-  /// \c S->getScope().
+  /// emits nothing if \c resolveScope returns no id for \c S->getScope().
   std::optional<MCRegister>
   emitDebugLexicalBlock(const DIScope *S, MCRegister VoidTypeReg,
                         MCRegister I32TypeReg, MCRegister ExtInstSetReg,

>From 37ef9ac9226b770da31c44551981f0844e9cf2a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Juan=20Manuel=20Martinez=20Caama=C3=B1o?=
 <jmartinezcaamao at gmail.com>
Date: Tue, 1 Sep 2026 11:14:39 +0200
Subject: [PATCH 2/2] [Review] remove asserts

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 26c071fe2e18d..2c2f9172f81c0 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -819,11 +819,10 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugGlobalVariable(
     SPIRV::ModuleAnalysisInfo &MAI) {
   assert(GV && "GV must not be null in emitDebugGlobalVariable");
 
-  assert(!CompileUnits.empty() &&
-         "emitDebugGlobalVariable requires non-empty CompileUnits");
   auto ParentRegOpt = resolveScope(GV->getScope());
-  assert(ParentRegOpt && "DebugCompilationUnit must be emitted before "
-                         "emitDebugGlobalVariable");
+  if (!ParentRegOpt)
+    return std::nullopt;
+
   MCRegister ParentReg = *ParentRegOpt;
 
   // TyReg: DebugInfoNone when GV has no DI type (as done in



More information about the llvm-branch-commits mailing list