[llvm-branch-commits] [llvm] [SPIRV] Emit each debug scope from getOrCreateDebugScope. (PR #229488)

Manuel Carrasco via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Oct 8 03:31:56 PDT 2026


https://github.com/mgcarrasco updated https://github.com/llvm/llvm-project/pull/229488

>From e862efc7984190feed9062da6c4cb7867b1735b5 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Tue, 6 Oct 2026 11:33:02 -0500
Subject: [PATCH] [SPIRV] Emit each debug scope from getOrCreateDebugScope.

Handlers return the opcode and the operands. getOrCreateDebugScope writes that instruction with a fresh id.
---
 .../SPIRV/SPIRVNonSemanticDebugHandler.cpp    | 344 +++++++++---------
 .../SPIRV/SPIRVNonSemanticDebugHandler.h      | 224 +++++++-----
 2 files changed, 296 insertions(+), 272 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 3ed9eefd6d34a..ad5871e10a619 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -542,6 +542,7 @@ MCRegister SPIRVNonSemanticDebugHandler::emitExtInst(
     MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
     ArrayRef<MCRegister> Operands, SPIRV::ModuleAnalysisInfo &MAI) {
   MCRegister Reg = MAI.getNextIDRegister();
+
   MCInst Inst;
   Inst.setOpcode(SPIRV::OpExtInst);
   Inst.addOperand(MCOperand::createReg(Reg));
@@ -761,34 +762,44 @@ MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeInt32(
 // Each node emits its dependencies first. E.g. for `void f() { struct L; }`,
 // node L emits f, then L. For `struct S { S *p; }`, S* is a back edge, so S
 // is emitted without member p.
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::getOrCreateDebugScope(const DIScope *S) {
+auto SPIRVNonSemanticDebugHandler::getOrCreateDebugScope(const DIScope *S)
+    -> EmitResult<MCRegister> {
   if (!S || FailedScopes.contains(S))
-    return EmitResult::unsupported();
+    return EmitResult<MCRegister>::unsupported();
   if (auto Reg = lookupOptReg(DebugScopeRegs, S))
-    return EmitResult::emitted(*Reg);
+    return EmitResult<MCRegister>::emitted(*Reg);
 
   // Mark the node as in progress while its dependencies are resolved/emitted.
   // This also prevents cycles from happening.
   if (!ScopesInProgress.insert(S).second)
-    return EmitResult::inProgress();
-  EmitResult R = emitDebugScope(S);
+    return EmitResult<MCRegister>::inProgress();
+
+  EmitResult<PreparedScope> P = emitDebugScope(S);
   ScopesInProgress.erase(S);
-  assert((!R || !GlobalNSDIEnabled) &&
+  assert((!P || !GlobalNSDIEnabled) &&
          "debug type or scope created after module-scope NSDI emission");
-  if (R)
-    DebugScopeRegs[S] = R.Reg;
-  else if (R.Status == EmitStatus::Unsupported)
-    FailedScopes.insert(S);
-  return R;
+  if (!P) {
+    if (P.Status == EmitStatus::Unsupported)
+      FailedScopes.insert(S);
+    return P.Status == EmitStatus::InProgress
+               ? EmitResult<MCRegister>::inProgress()
+               : EmitResult<MCRegister>::unsupported();
+  }
+
+  // The scope was successfully emitted.
+  SPIRV::ModuleAnalysisInfo &MAI = *CurrentMAI;
+  MCRegister Reg =
+      emitExtInst(P.Value->Opcode, getOrEmitOpTypeVoidReg(MAI),
+                  MAI.getExtInstSetReg(NSSet), P.Value->Operands, MAI);
+  DebugScopeRegs[S] = Reg;
+  return EmitResult<MCRegister>::emitted(Reg);
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugTypeBasic(
-    const DIBasicType *BT, MCRegister VoidTypeReg, MCRegister I32TypeReg,
-    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+auto SPIRVNonSemanticDebugHandler::emitDebugTypeBasic(
+    const DIBasicType *BT, MCRegister I32TypeReg,
+    SPIRV::ModuleAnalysisInfo &MAI) -> EmitResult<PreparedScope> {
   if (!isUInt<32>(BT->getSizeInBits()))
-    return EmitResult::unsupported();
+    return EmitResult<PreparedScope>::unsupported();
 
   MCRegister NameReg = getCachedOpStringReg(BT->getName());
   MCRegister SizeReg = emitOpConstantI32(
@@ -822,13 +833,13 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeBasic(
   }
   MCRegister EncodingReg = emitOpConstantI32(Encoding, I32TypeReg, MAI);
   MCRegister FlagsReg = emitOpConstantI32(0, I32TypeReg, MAI);
-  return EmitResult::emitted(emitExtInst(
-      SPIRV::NonSemanticExtInst::DebugTypeBasic, VoidTypeReg, ExtInstSetReg,
-      {NameReg, SizeReg, EncodingReg, FlagsReg}, MAI));
+  return EmitResult<PreparedScope>::emitted(
+      PreparedScope{SPIRV::NonSemanticExtInst::DebugTypeBasic,
+                    {NameReg, SizeReg, EncodingReg, FlagsReg}});
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugScope(const DIScope *S) {
+auto SPIRVNonSemanticDebugHandler::emitDebugScope(const DIScope *S)
+    -> EmitResult<PreparedScope> {
   assert(CurrentMAI && "emitDebugScope requires CurrentMAI");
   SPIRV::ModuleAnalysisInfo &MAI = *CurrentMAI;
   MCRegister ExtInstSetReg = MAI.getExtInstSetReg(NSSet);
@@ -836,9 +847,9 @@ SPIRVNonSemanticDebugHandler::emitDebugScope(const DIScope *S) {
   MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
 
   if (const auto *BT = dyn_cast<DIBasicType>(S))
-    return emitDebugTypeBasic(BT, VoidTypeReg, I32TypeReg, ExtInstSetReg, MAI);
+    return emitDebugTypeBasic(BT, I32TypeReg, MAI);
   if (const auto *ST = dyn_cast<DISubroutineType>(S))
-    return emitDebugTypeFunctionForSubroutineType(ST, ExtInstSetReg, MAI);
+    return emitDebugTypeFunctionForSubroutineType(ST, MAI);
   if (const auto *CT = dyn_cast<DICompositeType>(S))
     return emitDebugTypeForCompositeType(CT, VoidTypeReg, I32TypeReg,
                                          ExtInstSetReg, MAI);
@@ -854,13 +865,13 @@ SPIRVNonSemanticDebugHandler::emitDebugScope(const DIScope *S) {
     return emitDebugFunctionDeclaration(SP, VoidTypeReg, I32TypeReg,
                                         ExtInstSetReg, MAI);
   }
-  return EmitResult::unsupported();
+  return EmitResult<PreparedScope>::unsupported();
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugTypeForCompositeType(
+auto SPIRVNonSemanticDebugHandler::emitDebugTypeForCompositeType(
     const DICompositeType *CT, MCRegister VoidTypeReg, MCRegister I32TypeReg,
-    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
   switch (CT->getTag()) {
   case dwarf::DW_TAG_array_type:
     // A vector is an array with DINode::FlagVector. A plain array is the
@@ -875,49 +886,48 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeForCompositeType(
     // not disambiguate a matrix from a genuine array of vectors. Once the
     // frontend marks matrices, route them to a DebugTypeMatrix path here.
     if (CT->isVector())
-      return emitDebugTypeVector(CT, ExtInstSetReg, MAI);
-    return emitDebugTypeArray(CT, ExtInstSetReg, MAI);
+      return emitDebugTypeVector(CT, MAI);
+    return emitDebugTypeArray(CT, MAI);
   case dwarf::DW_TAG_structure_type:
   case dwarf::DW_TAG_class_type:
   case dwarf::DW_TAG_union_type:
     return emitDebugTypeComposite(CT, VoidTypeReg, I32TypeReg, ExtInstSetReg,
                                   MAI);
   default:
-    return EmitResult::unsupported();
+    return EmitResult<PreparedScope>::unsupported();
   }
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugTypeForDerivedType(
+auto SPIRVNonSemanticDebugHandler::emitDebugTypeForDerivedType(
     const DIDerivedType *DT, MCRegister VoidTypeReg, MCRegister I32TypeReg,
-    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
   switch (DT->getTag()) {
   case dwarf::DW_TAG_pointer_type:
-    return emitDebugTypePointer(DT, ExtInstSetReg, MAI);
+    return emitDebugTypePointer(DT, MAI);
   case dwarf::DW_TAG_typedef:
     return emitDebugTypedef(DT, VoidTypeReg, I32TypeReg, ExtInstSetReg, MAI);
   case dwarf::DW_TAG_member:
     return emitDebugTypeMember(DT, VoidTypeReg, I32TypeReg, ExtInstSetReg, MAI);
   default:
-    return EmitResult::unsupported();
+    return EmitResult<PreparedScope>::unsupported();
   }
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
-    const DIDerivedType *PT, MCRegister ExtInstSetReg,
-    SPIRV::ModuleAnalysisInfo &MAI) {
+auto SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
+    const DIDerivedType *PT, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
   // A DWARF address space is required to determine the SPIR-V storage class.
   // Skip pointer types that do not carry one.
   if (!PT->getDWARFAddressSpace().has_value())
-    return EmitResult::unsupported();
+    return EmitResult<PreparedScope>::unsupported();
 
   MCRegister BaseReg;
   if (const DIType *BaseTy = PT->getBaseType()) {
-    EmitResult Base = getOrCreateDebugScope(BaseTy);
+    EmitResult<MCRegister> Base = getOrCreateDebugScope(BaseTy);
     if (!Base)
-      return Base;
-    BaseReg = Base.Reg;
+      return EmitResult<PreparedScope>::failure(Base);
+    BaseReg = *Base.Value;
   } else {
     // No getBaseType() (typical for void*): use DebugInfoNone as Base Type,
     // same as SPIRV-LLVM-Translator (see issue #109287 and the DISABLED
@@ -926,7 +936,6 @@ SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
     BaseReg = CachedDebugInfoNoneReg;
   }
 
-  MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
   MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
   MCRegister DebugTypePointerFlagsReg =
       emitOpConstantI32(transDebugFlags(PT), I32TypeReg, MAI);
@@ -938,15 +947,14 @@ SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
       addressSpaceToStorageClass(PT->getDWARFAddressSpace().value(), ST),
       I32TypeReg, MAI);
 
-  return EmitResult::emitted(emitExtInst(
-      SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg, ExtInstSetReg,
-      {BaseReg, StorageClassReg, DebugTypePointerFlagsReg}, MAI));
+  return EmitResult<PreparedScope>::emitted(
+      PreparedScope{SPIRV::NonSemanticExtInst::DebugTypePointer,
+                    {BaseReg, StorageClassReg, DebugTypePointerFlagsReg}});
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
-    const DISubroutineType *ST, MCRegister ExtInstSetReg,
-    SPIRV::ModuleAnalysisInfo &MAI) {
+auto SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
+    const DISubroutineType *ST, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
   MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
   MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
   MCRegister DebugTypeFunctionFlagsReg =
@@ -962,22 +970,21 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
   } else {
     for (unsigned I = 0, E = TA.size(); I != E; ++I) {
       bool IsReturnType = (I == 0);
-      EmitResult Slot =
+      EmitResult<MCRegister> Slot =
           mapDISignatureTypeToReg(TA[I], VoidTypeReg, IsReturnType);
       // No emitted DebugType* id for this slot (e.g., pointer that
       // was skipped due missing address space, etc.).
       if (!Slot)
-        return Slot;
-      Ops.push_back(Slot.Reg);
+        return EmitResult<PreparedScope>::failure(Slot);
+      Ops.push_back(*Slot.Value);
     }
   }
-  return EmitResult::emitted(
-      emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeFunction, VoidTypeReg,
-                  ExtInstSetReg, Ops, MAI));
+  return EmitResult<PreparedScope>::emitted(
+      PreparedScope{SPIRV::NonSemanticExtInst::DebugTypeFunction, Ops});
 }
 
 // Match SPIRV-LLVM-Translator's selection logic for the Parent operand.
-SPIRVNonSemanticDebugHandler::EmitResult
+SPIRVNonSemanticDebugHandler::EmitResult<MCRegister>
 SPIRVNonSemanticDebugHandler::resolveScope(const DIScope *Scope,
                                            const DICompileUnit *FallbackCU) {
   // DebugLexicalBlockDiscriminator cannot be used as a scope, so unwrap it.
@@ -989,26 +996,27 @@ SPIRVNonSemanticDebugHandler::resolveScope(const DIScope *Scope,
 
   // For a file, compile-unit, or absent scope, fall back to a compile unit.
   auto CUResult = [](std::optional<MCRegister> Reg) {
-    return Reg ? EmitResult::emitted(*Reg) : EmitResult::unsupported();
+    return Reg ? EmitResult<MCRegister>::emitted(*Reg)
+               : EmitResult<MCRegister>::unsupported();
   };
   if (FallbackCU)
     return CUResult(lookupOptReg(DebugScopeRegs, FallbackCU));
 
   if (CompileUnits.empty())
-    return EmitResult::unsupported();
+    return EmitResult<MCRegister>::unsupported();
 
   return CUResult(lookupOptReg(DebugScopeRegs, CompileUnits[0].TheCU));
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugLexicalBlock(
+auto SPIRVNonSemanticDebugHandler::emitDebugLexicalBlock(
     const DIScope *S, MCRegister VoidTypeReg, MCRegister I32TypeReg,
-    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
   assert((isa<DILexicalBlock, DINamespace>(S)) &&
          "S must be a DILexicalBlock or DINamespace in emitDebugLexicalBlock");
-  EmitResult Parent = resolveScope(S->getScope());
+  EmitResult<MCRegister> Parent = resolveScope(S->getScope());
   if (!Parent)
-    return Parent;
+    return EmitResult<PreparedScope>::failure(Parent);
 
   MCRegister FileStrReg = getCachedScopePathOpStringReg(
       S->getFile(), /*UseEmptyPathIfNullScope=*/true);
@@ -1021,19 +1029,18 @@ SPIRVNonSemanticDebugHandler::emitDebugLexicalBlock(
                                            I32TypeReg, MAI);
     MCRegister ColReg = emitOpConstantI32(
         static_cast<uint32_t>(LB->getColumn()), I32TypeReg, MAI);
-    Ops = {SrcReg, LineReg, ColReg, Parent.Reg};
+    Ops = {SrcReg, LineReg, ColReg, *Parent.Value};
   } else {
     const auto *NS = cast<DINamespace>(S);
     // DINamespace carries no line/column info.
     MCRegister LineReg = emitOpConstantI32(0, I32TypeReg, MAI);
     MCRegister ColReg = emitOpConstantI32(0, I32TypeReg, MAI);
     MCRegister NameReg = getCachedOpStringReg(NS->getName());
-    Ops = {SrcReg, LineReg, ColReg, Parent.Reg, NameReg};
+    Ops = {SrcReg, LineReg, ColReg, *Parent.Value, NameReg};
   }
 
-  return EmitResult::emitted(
-      emitExtInst(SPIRV::NonSemanticExtInst::DebugLexicalBlock, VoidTypeReg,
-                  ExtInstSetReg, Ops, MAI));
+  return EmitResult<PreparedScope>::emitted(
+      PreparedScope{SPIRV::NonSemanticExtInst::DebugLexicalBlock, Ops});
 }
 
 MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugInlinedAt(
@@ -1044,14 +1051,14 @@ MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugInlinedAt(
   if (MCRegister Cached = DebugInlinedAtRegs.lookup(IA))
     return Cached;
 
-  EmitResult Scope = resolveScope(IA->getScope());
+  EmitResult<MCRegister> Scope = resolveScope(IA->getScope());
   if (!Scope)
     return MCRegister();
 
   MCRegister LineReg =
       emitOpConstantI32(static_cast<uint32_t>(IA->getLine()), I32TypeReg, MAI);
 
-  SmallVector<MCRegister, 3> Ops{LineReg, Scope.Reg};
+  SmallVector<MCRegister, 3> Ops{LineReg, *Scope.Value};
   // Recurse before building this instruction's operands so an outer
   // inlined-at link is always available.
   if (const DILocation *Outer = IA->getInlinedAt()) {
@@ -1068,22 +1075,22 @@ MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugInlinedAt(
   return Reg;
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugFunctionDeclaration(
+auto SPIRVNonSemanticDebugHandler::emitDebugFunctionDeclaration(
     const DISubprogram *SP, MCRegister VoidTypeReg, MCRegister I32TypeReg,
-    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
   assert(SP && "SP must not be null in emitDebugFunctionDeclaration");
   assert(!SP->isDefinition() &&
          "SP must not be a definition in emitDebugFunctionDeclaration");
 
   // The IR verifier already enforces that this cannot be null.
-  EmitResult FnTy = getOrCreateDebugScope(SP->getType());
+  EmitResult<MCRegister> FnTy = getOrCreateDebugScope(SP->getType());
   if (!FnTy)
-    return FnTy;
+    return EmitResult<PreparedScope>::failure(FnTy);
 
-  EmitResult Parent = resolveScope(SP->getScope(), SP->getUnit());
+  EmitResult<MCRegister> Parent = resolveScope(SP->getScope(), SP->getUnit());
   if (!Parent)
-    return Parent;
+    return EmitResult<PreparedScope>::failure(Parent);
 
   MCRegister FileStrReg = getCachedScopePathOpStringReg(SP);
 
@@ -1102,28 +1109,26 @@ SPIRVNonSemanticDebugHandler::emitDebugFunctionDeclaration(
   FlagsVal &= ~NSDIFlagIsDefinition;
   MCRegister FlagsReg = emitOpConstantI32(FlagsVal, I32TypeReg, MAI);
 
-  return EmitResult::emitted(
-      emitExtInst(SPIRV::NonSemanticExtInst::DebugFunctionDeclaration,
-                  VoidTypeReg, ExtInstSetReg,
-                  {NameReg, FnTy.Reg, SrcReg, LineReg, ColReg, Parent.Reg,
-                   LinkageReg, FlagsReg},
-                  MAI));
+  return EmitResult<PreparedScope>::emitted(
+      PreparedScope{SPIRV::NonSemanticExtInst::DebugFunctionDeclaration,
+                    {NameReg, *FnTy.Value, SrcReg, LineReg, ColReg,
+                     *Parent.Value, LinkageReg, FlagsReg}});
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugFunction(
+auto SPIRVNonSemanticDebugHandler::emitDebugFunction(
     const DISubprogram *SP, MCRegister VoidTypeReg, MCRegister I32TypeReg,
-    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
   assert(SP && "SP must not be null in emitDebugFunction");
   assert(SP->isDefinition() && "SP must be a definition in emitDebugFunction");
 
-  EmitResult FnTy = getOrCreateDebugScope(SP->getType());
+  EmitResult<MCRegister> FnTy = getOrCreateDebugScope(SP->getType());
   if (!FnTy)
-    return FnTy;
+    return EmitResult<PreparedScope>::failure(FnTy);
 
-  EmitResult Parent = resolveScope(SP->getScope(), SP->getUnit());
+  EmitResult<MCRegister> Parent = resolveScope(SP->getScope(), SP->getUnit());
   if (!Parent)
-    return Parent;
+    return EmitResult<PreparedScope>::failure(Parent);
 
   MCRegister NameReg = getCachedOpStringReg(SP->getName());
   MCRegister LinkageReg = getCachedOpStringReg(SP->getLinkageName());
@@ -1140,9 +1145,9 @@ SPIRVNonSemanticDebugHandler::emitDebugFunction(
   MCRegister ScopeLineReg = emitOpConstantI32(
       static_cast<uint32_t>(SP->getScopeLine()), I32TypeReg, MAI);
 
-  SmallVector<MCRegister, 10> Ops = {NameReg,    FnTy.Reg, SrcReg,
-                                     LineReg,    ColReg,   Parent.Reg,
-                                     LinkageReg, FlagsReg, ScopeLineReg};
+  SmallVector<MCRegister, 10> Ops = {NameReg,    *FnTy.Value, SrcReg,
+                                     LineReg,    ColReg,      *Parent.Value,
+                                     LinkageReg, FlagsReg,    ScopeLineReg};
 
   // TODO: Create on demand once beginModule collects missing getDeclaration()
   // cases.
@@ -1151,21 +1156,19 @@ SPIRVNonSemanticDebugHandler::emitDebugFunction(
       Ops.push_back(*DeclRegOpt);
   }
 
-  return EmitResult::emitted(
-      emitExtInst(SPIRV::NonSemanticExtInst::DebugFunction, VoidTypeReg,
-                  ExtInstSetReg, Ops, MAI));
+  return EmitResult<PreparedScope>::emitted(
+      PreparedScope{SPIRV::NonSemanticExtInst::DebugFunction, Ops});
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(const DIType *Ty,
-                                                      MCRegister VoidTypeReg,
-                                                      bool ReturnType) {
+auto SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
+    const DIType *Ty, MCRegister VoidTypeReg, bool ReturnType)
+    -> EmitResult<MCRegister> {
   if (!Ty) {
     if (ReturnType)
-      return EmitResult::emitted(VoidTypeReg);
+      return EmitResult<MCRegister>::emitted(VoidTypeReg);
     assert(CachedDebugInfoNoneReg.isValid() &&
            "DebugInfoNone must be emitted before DISubroutineType operands");
-    return EmitResult::emitted(CachedDebugInfoNoneReg);
+    return EmitResult<MCRegister>::emitted(CachedDebugInfoNoneReg);
   }
   return getOrCreateDebugScope(Ty);
 }
@@ -1284,11 +1287,11 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugGlobalVariable(
     SPIRV::ModuleAnalysisInfo &MAI) {
   assert(GV && "GV must not be null in emitDebugGlobalVariable");
 
-  EmitResult Parent = resolveScope(GV->getScope());
+  EmitResult<MCRegister> Parent = resolveScope(GV->getScope());
   if (!Parent)
     return std::nullopt;
 
-  MCRegister ParentReg = Parent.Reg;
+  MCRegister ParentReg = *Parent.Value;
 
   // TyReg: DebugInfoNone when GV has no DI type (as done in
   // SPIRV-LLVM-Translator). Declarations (isDefinition: false) can have null
@@ -1304,10 +1307,10 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugGlobalVariable(
 
   std::optional<MCRegister> StaticMemberRegOpt;
   if (const DIDerivedType *SM = GV->getStaticDataMemberDeclaration()) {
-    EmitResult StaticMember = getOrCreateDebugScope(SM);
+    EmitResult<MCRegister> StaticMember = getOrCreateDebugScope(SM);
     if (!StaticMember)
       return std::nullopt;
-    StaticMemberRegOpt = StaticMember.Reg;
+    StaticMemberRegOpt = *StaticMember.Value;
   }
 
   MCRegister NameReg = getCachedOpStringReg(GV->getName());
@@ -1358,7 +1361,7 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugLocalVariable(
     MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
   assert(LV && "LV must not be null in emitDebugLocalVariable");
 
-  EmitResult Parent = resolveScope(LV->getScope());
+  EmitResult<MCRegister> Parent = resolveScope(LV->getScope());
   if (!Parent)
     return std::nullopt;
 
@@ -1381,8 +1384,8 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugLocalVariable(
   MCRegister ColReg = emitOpConstantI32(0, I32TypeReg, MAI);
   MCRegister FlagsReg = emitOpConstantI32(transDebugFlags(LV), I32TypeReg, MAI);
 
-  SmallVector<MCRegister, 8> Ops = {NameReg, TyReg,      SrcReg,  LineReg,
-                                    ColReg,  Parent.Reg, FlagsReg};
+  SmallVector<MCRegister, 8> Ops = {NameReg, TyReg,         SrcReg,  LineReg,
+                                    ColReg,  *Parent.Value, FlagsReg};
   if (unsigned Arg = LV->getArg())
     Ops.push_back(emitOpConstantI32(Arg, I32TypeReg, MAI));
 
@@ -1390,52 +1393,47 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugLocalVariable(
                      ExtInstSetReg, Ops, MAI);
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugTypeVector(
-    const DICompositeType *VT, MCRegister ExtInstSetReg,
-    SPIRV::ModuleAnalysisInfo &MAI) {
+auto SPIRVNonSemanticDebugHandler::emitDebugTypeVector(
+    const DICompositeType *VT, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
   const auto *BaseTy = dyn_cast_or_null<DIBasicType>(VT->getBaseType());
   if (!BaseTy)
-    return EmitResult::unsupported();
+    return EmitResult<PreparedScope>::unsupported();
 
   // DebugTypeVector models only 1D vectors (multi-subrange types cannot be
   // encoded).
   DINodeArray Elements = VT->getElements();
   if (Elements.size() != 1)
-    return EmitResult::unsupported();
+    return EmitResult<PreparedScope>::unsupported();
   const auto *SR = cast<DISubrange>(Elements[0]);
   const auto *CI = dyn_cast_if_present<ConstantInt *>(SR->getCount());
   if (!CI)
-    return EmitResult::unsupported();
+    return EmitResult<PreparedScope>::unsupported();
 
-  EmitResult Base = getOrCreateDebugScope(BaseTy);
+  EmitResult<MCRegister> Base = getOrCreateDebugScope(BaseTy);
   if (!Base)
-    return Base;
+    return EmitResult<PreparedScope>::failure(Base);
 
-  MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
   MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
   MCRegister CountReg = emitOpConstantI32(
       static_cast<uint32_t>(CI->getZExtValue()), I32TypeReg, MAI);
-  return EmitResult::emitted(
-      emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeVector, VoidTypeReg,
-                  ExtInstSetReg, {Base.Reg, CountReg}, MAI));
+  return EmitResult<PreparedScope>::emitted(PreparedScope{
+      SPIRV::NonSemanticExtInst::DebugTypeVector, {*Base.Value, CountReg}});
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugTypeArray(
-    const DICompositeType *AT, MCRegister ExtInstSetReg,
-    SPIRV::ModuleAnalysisInfo &MAI) {
+auto SPIRVNonSemanticDebugHandler::emitDebugTypeArray(
+    const DICompositeType *AT, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
   // Unlike DebugTypeVector, the element may be any debug type, not only a
   // basic type. Create it first so this array names an id that already exists.
-  EmitResult Base = getOrCreateDebugScope(AT->getBaseType());
+  EmitResult<MCRegister> Base = getOrCreateDebugScope(AT->getBaseType());
   if (!Base)
-    return Base;
+    return EmitResult<PreparedScope>::failure(Base);
 
-  MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
   MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
 
   SmallVector<MCRegister> Ops;
-  Ops.push_back(Base.Reg);
+  Ops.push_back(*Base.Value);
 
   // One component count per DISubrange, in DWARF subrange order. Emit 0 for
   // counts that are not a compile-time constant (dynamic arrays). This matches
@@ -1458,21 +1456,20 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeArray(
     Ops.push_back(emitOpConstantI32(Count, I32TypeReg, MAI));
   }
 
-  return EmitResult::emitted(
-      emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeArray, VoidTypeReg,
-                  ExtInstSetReg, Ops, MAI));
+  return EmitResult<PreparedScope>::emitted(
+      PreparedScope{SPIRV::NonSemanticExtInst::DebugTypeArray, Ops});
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugTypeMember(
+auto SPIRVNonSemanticDebugHandler::emitDebugTypeMember(
     const DIDerivedType *M, MCRegister VoidTypeReg, MCRegister I32TypeReg,
-    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
-  EmitResult Ty = getOrCreateDebugScope(M->getBaseType());
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
+  EmitResult<MCRegister> Ty = getOrCreateDebugScope(M->getBaseType());
   if (!Ty)
-    return Ty;
+    return EmitResult<PreparedScope>::failure(Ty);
 
   if (!isUInt<32>(M->getOffsetInBits()) || !isUInt<32>(M->getSizeInBits()))
-    return EmitResult::unsupported();
+    return EmitResult<PreparedScope>::unsupported();
 
   MCRegister NameReg = getCachedOpStringReg(M->getName());
   MCRegister FileStrReg = getCachedScopePathOpStringReg(
@@ -1499,33 +1496,33 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeMember(
   // available but is not emitted as the optional Value operand, and under DWARF
   // 5 a static member is tagged DW_TAG_variable, which the caller's member loop
   // skips.
-  return EmitResult::emitted(emitExtInst(
-      SPIRV::NonSemanticExtInst::DebugTypeMember, VoidTypeReg, ExtInstSetReg,
-      {NameReg, Ty.Reg, SrcReg, LineReg, ColReg, OffsetReg, SizeReg, FlagsReg},
-      MAI));
+  return EmitResult<PreparedScope>::emitted(
+      PreparedScope{SPIRV::NonSemanticExtInst::DebugTypeMember,
+                    {NameReg, *Ty.Value, SrcReg, LineReg, ColReg, OffsetReg,
+                     SizeReg, FlagsReg}});
 }
 
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugTypeComposite(
+auto SPIRVNonSemanticDebugHandler::emitDebugTypeComposite(
     const DICompositeType *CT, MCRegister VoidTypeReg, MCRegister I32TypeReg,
-    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
   // Parent and size before any member: a failure must stream nothing, or a
   // retry would emit the members again.
-  EmitResult Parent = resolveScope(CT->getScope());
+  EmitResult<MCRegister> Parent = resolveScope(CT->getScope());
   if (!Parent)
-    return Parent;
+    return EmitResult<PreparedScope>::failure(Parent);
 
   if (!isUInt<32>(CT->getSizeInBits()))
-    return EmitResult::unsupported();
+    return EmitResult<PreparedScope>::unsupported();
 
   SmallVector<MCRegister, 8> MemberRegs;
   for (const DINode *Element : CT->getElements()) {
     const auto *M = dyn_cast<DIDerivedType>(Element);
     if (!M || M->getTag() != dwarf::DW_TAG_member)
       continue;
-    EmitResult Member = getOrCreateDebugScope(M);
+    EmitResult<MCRegister> Member = getOrCreateDebugScope(M);
     if (Member)
-      MemberRegs.push_back(Member.Reg);
+      MemberRegs.push_back(*Member.Value);
   }
 
   MCRegister NameReg = getCachedOpStringReg(CT->getName());
@@ -1550,23 +1547,20 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeComposite(
   MCRegister FlagsReg = emitOpConstantI32(transDebugFlags(CT), I32TypeReg, MAI);
 
   SmallVector<MCRegister> Ops = {NameReg,    TagReg,  SrcReg,
-                                 LineReg,    ColReg,  Parent.Reg,
+                                 LineReg,    ColReg,  *Parent.Value,
                                  LinkageReg, SizeReg, FlagsReg};
   Ops.append(MemberRegs.begin(), MemberRegs.end());
-  return EmitResult::emitted(
-      emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeComposite, VoidTypeReg,
-                  ExtInstSetReg, Ops, MAI));
-}
-
-SPIRVNonSemanticDebugHandler::EmitResult
-SPIRVNonSemanticDebugHandler::emitDebugTypedef(const DIDerivedType *TD,
-                                               MCRegister VoidTypeReg,
-                                               MCRegister I32TypeReg,
-                                               MCRegister ExtInstSetReg,
-                                               SPIRV::ModuleAnalysisInfo &MAI) {
-  EmitResult Base = getOrCreateDebugScope(TD->getBaseType());
+  return EmitResult<PreparedScope>::emitted(
+      PreparedScope{SPIRV::NonSemanticExtInst::DebugTypeComposite, Ops});
+}
+
+auto SPIRVNonSemanticDebugHandler::emitDebugTypedef(
+    const DIDerivedType *TD, MCRegister VoidTypeReg, MCRegister I32TypeReg,
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI)
+    -> EmitResult<PreparedScope> {
+  EmitResult<MCRegister> Base = getOrCreateDebugScope(TD->getBaseType());
   if (!Base)
-    return Base;
+    return EmitResult<PreparedScope>::failure(Base);
 
   MCRegister NameReg = getCachedOpStringReg(TD->getName());
   MCRegister FileStrReg = getCachedScopePathOpStringReg(
@@ -1581,13 +1575,13 @@ SPIRVNonSemanticDebugHandler::emitDebugTypedef(const DIDerivedType *TD,
   // Parent must be a lexical scope. Valid NSDI lexical scopes are
   // DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or
   // DebugTypeComposite.
-  EmitResult Parent = resolveScope(TD->getScope());
+  EmitResult<MCRegister> Parent = resolveScope(TD->getScope());
   if (!Parent)
-    return Parent;
+    return EmitResult<PreparedScope>::failure(Parent);
 
-  return EmitResult::emitted(emitExtInst(
-      SPIRV::NonSemanticExtInst::DebugTypedef, VoidTypeReg, ExtInstSetReg,
-      {NameReg, Base.Reg, SrcReg, LineReg, ColReg, Parent.Reg}, MAI));
+  return EmitResult<PreparedScope>::emitted(PreparedScope{
+      SPIRV::NonSemanticExtInst::DebugTypedef,
+      {NameReg, *Base.Value, SrcReg, LineReg, ColReg, *Parent.Value}});
 }
 
 void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
@@ -2093,11 +2087,11 @@ bool SPIRVNonSemanticDebugHandler::emitDebugScopeForInstruction(
       return true;
   }
 
-  EmitResult CurScopeRes = resolveScope(CurScope);
+  EmitResult<MCRegister> CurScopeRes = resolveScope(CurScope);
   if (!CurScopeRes)
     return false;
 
-  SmallVector<MCRegister, 2> Ops{CurScopeRes.Reg};
+  SmallVector<MCRegister, 2> Ops{*CurScopeRes.Value};
   if (CurInlinedAt) {
     // If the global emission did not include this inlined-at case, we skip it.
     MCRegister InlinedReg = DebugInlinedAtRegs.lookup(CurInlinedAt);
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index efbabd280ea24..8e17d8cf0d39b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -32,6 +32,8 @@
 #include "llvm/MC/MCInst.h"
 #include "llvm/MC/MCRegister.h"
 #include <optional>
+#include <type_traits>
+#include <utility>
 
 namespace llvm {
 
@@ -81,21 +83,52 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
     InProgress,
   };
 
-  struct EmitResult {
-    EmitStatus Status;
-    MCRegister Reg;
+  // One scope instruction, built but not yet emitted. getOrCreateDebugScope
+  // is the only place that emits it.
+  struct PreparedScope {
+    SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode{};
+    SmallVector<MCRegister, 8> Operands;
 
-    static EmitResult emitted(MCRegister R) { return {EmitStatus::Emitted, R}; }
-    static EmitResult unsupported() {
-      return {EmitStatus::Unsupported, MCRegister()};
+    PreparedScope() = default;
+    PreparedScope(SPIRV::NonSemanticExtInst::NonSemanticExtInst Op,
+                  ArrayRef<MCRegister> Ops)
+        : Opcode(Op), Operands(Ops.begin(), Ops.end()) {}
+  };
+
+  // Status plus a payload. Value is set only when the status is Emitted, so
+  // InProgress and Unsupported carry no id and no PreparedScope. The payload
+  // is an id for a scope that already exists, and a PreparedScope for an
+  // instruction that getOrCreateDebugScope has not emitted yet.
+  template <typename T> struct EmitResult {
+    EmitStatus Status = EmitStatus::Unsupported;
+    std::optional<T> Value;
+
+    static EmitResult emitted(T V) {
+      EmitResult R;
+      R.Status = EmitStatus::Emitted;
+      R.Value = std::move(V);
+      return R;
     }
+    static EmitResult unsupported() { return {}; }
     static EmitResult inProgress() {
-      return {EmitStatus::InProgress, MCRegister()};
+      EmitResult R;
+      R.Status = EmitStatus::InProgress;
+      return R;
+    }
+
+    template <typename U>
+    static EmitResult failure(const EmitResult<U> &Other) {
+      assert(!Other && "failure() requires a non-emitted result");
+      EmitResult R;
+      R.Status = Other.Status;
+      return R;
     }
 
     explicit operator bool() const {
-      assert((Status != EmitStatus::Emitted || Reg.isValid()) &&
-             "emitted EmitResult without a result id");
+      assert(Value.has_value() == (Status == EmitStatus::Emitted) &&
+             "payload is set exactly when the result is emitted");
+      if constexpr (std::is_same_v<T, MCRegister>)
+        assert(!Value || Value->isValid());
       return Status == EmitStatus::Emitted;
     }
   };
@@ -378,58 +411,54 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// \c ScopesInProgress is the back edge of a cycle and is InProgress; nothing
   /// is emitted for that edge. An Unsupported result is recorded in
   /// \c FailedScopes, an InProgress one is not.
-  EmitResult getOrCreateDebugScope(const DIScope *S);
-
-  /// Emit the instruction for \p S. Only \c getOrCreateDebugScope calls this
-  /// and records the result.
-  EmitResult emitDebugScope(const DIScope *S);
-
-  /// Emit the \c DebugType* instruction for \p CT, picked by its tag.
-  EmitResult emitDebugTypeForCompositeType(const DICompositeType *CT,
-                                           MCRegister VoidTypeReg,
-                                           MCRegister I32TypeReg,
-                                           MCRegister ExtInstSetReg,
-                                           SPIRV::ModuleAnalysisInfo &MAI);
-
-  /// Emit the \c DebugType* instruction for \p DT, picked by its tag.
-  EmitResult emitDebugTypeForDerivedType(const DIDerivedType *DT,
-                                         MCRegister VoidTypeReg,
-                                         MCRegister I32TypeReg,
-                                         MCRegister ExtInstSetReg,
-                                         SPIRV::ModuleAnalysisInfo &MAI);
+  EmitResult<MCRegister> getOrCreateDebugScope(const DIScope *S);
+
+  /// Build the instruction for \p S. Only \c getOrCreateDebugScope calls this
+  /// and emits it.
+  EmitResult<PreparedScope> emitDebugScope(const DIScope *S);
 
-  /// Emit \c DebugTypeBasic for \p BT. Unsupported when the size does not fit
+  /// Build the \c DebugType* instruction for \p CT, picked by its tag.
+  EmitResult<PreparedScope> emitDebugTypeForCompositeType(
+      const DICompositeType *CT, MCRegister VoidTypeReg, MCRegister I32TypeReg,
+      MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Build the \c DebugType* instruction for \p DT, picked by its tag.
+  EmitResult<PreparedScope>
+  emitDebugTypeForDerivedType(const DIDerivedType *DT, MCRegister VoidTypeReg,
+                              MCRegister I32TypeReg, MCRegister ExtInstSetReg,
+                              SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Build \c DebugTypeBasic for \p BT. Unsupported when the size does not fit
   /// in 32 bits.
-  EmitResult emitDebugTypeBasic(const DIBasicType *BT, MCRegister VoidTypeReg,
-                                MCRegister I32TypeReg, MCRegister ExtInstSetReg,
-                                SPIRV::ModuleAnalysisInfo &MAI);
+  EmitResult<PreparedScope> emitDebugTypeBasic(const DIBasicType *BT,
+                                               MCRegister I32TypeReg,
+                                               SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Emit \c DebugTypePointer for \p PT. Unsupported without a DWARF address
+  /// Build \c DebugTypePointer for \p PT. Unsupported without a DWARF address
   /// space, which gives the storage class.
-  EmitResult emitDebugTypePointer(const DIDerivedType *PT,
-                                  MCRegister ExtInstSetReg,
-                                  SPIRV::ModuleAnalysisInfo &MAI);
+  EmitResult<PreparedScope>
+  emitDebugTypePointer(const DIDerivedType *PT, SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Emit one DebugTypeFunction for ST when every DI operand maps to a debug
-  /// type id; otherwise emit nothing and return the first failing slot's
+  /// Build one DebugTypeFunction for ST when every DI operand maps to a debug
+  /// type id; otherwise build nothing and return the first failing slot's
   /// status.
-  EmitResult
+  EmitResult<PreparedScope>
   emitDebugTypeFunctionForSubroutineType(const DISubroutineType *ST,
-                                         MCRegister ExtInstSetReg,
                                          SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Emit \c DebugFunctionDeclaration for the non-defining subprogram \p SP.
-  EmitResult emitDebugFunctionDeclaration(const DISubprogram *SP,
-                                          MCRegister VoidTypeReg,
-                                          MCRegister I32TypeReg,
-                                          MCRegister ExtInstSetReg,
-                                          SPIRV::ModuleAnalysisInfo &MAI);
-
-  /// Emit \c DebugFunction for the defining subprogram \p SP.
-  EmitResult emitDebugFunction(const DISubprogram *SP, MCRegister VoidTypeReg,
+  /// Build \c DebugFunctionDeclaration for the non-defining subprogram \p SP.
+  EmitResult<PreparedScope>
+  emitDebugFunctionDeclaration(const DISubprogram *SP, MCRegister VoidTypeReg,
                                MCRegister I32TypeReg, MCRegister ExtInstSetReg,
                                SPIRV::ModuleAnalysisInfo &MAI);
 
+  /// Build \c DebugFunction for the defining subprogram \p SP.
+  EmitResult<PreparedScope> emitDebugFunction(const DISubprogram *SP,
+                                              MCRegister VoidTypeReg,
+                                              MCRegister I32TypeReg,
+                                              MCRegister ExtInstSetReg,
+                                              SPIRV::ModuleAnalysisInfo &MAI);
+
   /// Emit \c DebugLocalVariable for the source local variable \p LV:
   /// Name, Type, Source, Line, Column, Parent, Flags, and an optional Arg
   /// Number. Line, Column, Flags, and Arg Number are emitted as \c OpConstant
@@ -578,67 +607,67 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   void emitDebugBinding(SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode,
                         const MachineInstr *MI, MCRegister LocationReg);
 
-  /// Emit \c DebugTypeVector for the vector composite type \p VT.
+  /// Build \c DebugTypeVector for the vector composite type \p VT.
   ///
-  /// \returns Emitted with the result id on success. Unsupported, emitting
+  /// \returns Emitted when the operands are ready. Unsupported, building
   /// nothing, if \p VT has no \c DIBasicType base type, if \p VT has more than
   /// one \c DISubrange element, or if the component count is not a
   /// compile-time constant. If the base cannot be emitted, returns its status.
-  EmitResult emitDebugTypeVector(const DICompositeType *VT,
-                                 MCRegister ExtInstSetReg,
-                                 SPIRV::ModuleAnalysisInfo &MAI);
+  EmitResult<PreparedScope> emitDebugTypeVector(const DICompositeType *VT,
+                                                SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Emit \c DebugTypeArray for the array composite type \p AT.
+  /// Build \c DebugTypeArray for the array composite type \p AT.
   ///
-  /// Emits the element (base) type id followed by one Component Count per
-  /// \c DISubrange, in DWARF subrange order. A count that is not a
-  /// compile-time constant is emitted as 0, matching \c OpTypeRuntimeArray. A
-  /// matrix arrives here as a multi-subrange array and is emitted with one
-  /// count per dimension.
+  /// Operands are the element (base) type id followed by one Component Count
+  /// per \c DISubrange, in DWARF subrange order. A count that is not a
+  /// compile-time constant is 0, matching \c OpTypeRuntimeArray. A matrix
+  /// arrives here as a multi-subrange array and is built with one count per
+  /// dimension.
   ///
-  /// \returns Emitted with the result id on success. If \p AT's element type
-  /// cannot be emitted, emits nothing and returns the element's status.
-  EmitResult emitDebugTypeArray(const DICompositeType *AT,
-                                MCRegister ExtInstSetReg,
-                                SPIRV::ModuleAnalysisInfo &MAI);
+  /// \returns Emitted when the operands are ready. If \p AT's element type
+  /// cannot be emitted, builds nothing and returns the element's status.
+  EmitResult<PreparedScope> emitDebugTypeArray(const DICompositeType *AT,
+                                               SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Emit \c DebugTypeMember for the data member \p M (a \c DIDerivedType with
+  /// Build \c DebugTypeMember for the data member \p M (a \c DIDerivedType with
   /// \c DW_TAG_member). Operands: Name, Type, Source, Line, Column, Offset,
   /// Size, Flags. NonSemantic \c DebugTypeMember carries no Parent operand: the
   /// enclosing \c DebugTypeComposite references its members, not the reverse.
   ///
-  /// \returns Emitted with the result id on success. Returns the base's status
-  /// and emits nothing if \p M's type cannot be emitted.
-  EmitResult emitDebugTypeMember(const DIDerivedType *M, MCRegister VoidTypeReg,
-                                 MCRegister I32TypeReg,
-                                 MCRegister ExtInstSetReg,
-                                 SPIRV::ModuleAnalysisInfo &MAI);
-
-  /// Emit \c DebugTypeComposite for the struct, class, or union \p CT, after a
+  /// \returns Emitted when the operands are ready. Returns the base's status
+  /// and builds nothing if \p M's type cannot be emitted.
+  EmitResult<PreparedScope> emitDebugTypeMember(const DIDerivedType *M,
+                                                MCRegister VoidTypeReg,
+                                                MCRegister I32TypeReg,
+                                                MCRegister ExtInstSetReg,
+                                                SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Build \c DebugTypeComposite for the struct, class, or union \p CT, after a
   /// \c DebugTypeMember for each data member whose type can be emitted. A
-  /// forward declaration emits \c DebugInfoNone for Size and no members.
+  /// forward declaration uses \c DebugInfoNone for Size and no members.
   ///
-  /// \returns Emitted with the result id on success. Emits nothing and returns
+  /// \returns Emitted when the operands are ready. Builds nothing and returns
   /// the parent's status if the Parent scope cannot be resolved, or
   /// Unsupported if the size does not fit in 32 bits. Both are checked before
   /// any member is streamed. A member whose type fails is dropped, whatever
   /// the reason, and does not fail the composite.
-  EmitResult emitDebugTypeComposite(const DICompositeType *CT,
-                                    MCRegister VoidTypeReg,
-                                    MCRegister I32TypeReg,
-                                    MCRegister ExtInstSetReg,
-                                    SPIRV::ModuleAnalysisInfo &MAI);
+  EmitResult<PreparedScope>
+  emitDebugTypeComposite(const DICompositeType *CT, MCRegister VoidTypeReg,
+                         MCRegister I32TypeReg, MCRegister ExtInstSetReg,
+                         SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Emit \c DebugTypedef for the typedef derived type \p TD (a \c
+  /// Build \c DebugTypedef for the typedef derived type \p TD (a \c
   /// DIDerivedType with \c DW_TAG_typedef). Operands: Name, Base Type, Source,
   /// Line, Column, Parent. Parent is the enclosing type when \c TD->getScope()
   /// is an emitted \c DIType, otherwise the first module \c
   /// DebugCompilationUnit.
   ///
   /// \returns The base's or Parent's status if either fails.
-  EmitResult emitDebugTypedef(const DIDerivedType *TD, MCRegister VoidTypeReg,
-                              MCRegister I32TypeReg, MCRegister ExtInstSetReg,
-                              SPIRV::ModuleAnalysisInfo &MAI);
+  EmitResult<PreparedScope> emitDebugTypedef(const DIDerivedType *TD,
+                                             MCRegister VoidTypeReg,
+                                             MCRegister I32TypeReg,
+                                             MCRegister ExtInstSetReg,
+                                             SPIRV::ModuleAnalysisInfo &MAI);
 
   /// Map a \c DISubroutineType::getTypeArray() element to an operand register
   /// for
@@ -652,8 +681,9 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// \p Ty is null, this returns \p VoidTypeReg (\c OpTypeVoid). When
   /// \p ReturnType is false and \p Ty is null, this returns
   /// \c CachedDebugInfoNoneReg (\c DebugInfoNone).
-  EmitResult mapDISignatureTypeToReg(const DIType *Ty, MCRegister VoidTypeReg,
-                                     bool ReturnType);
+  EmitResult<MCRegister> mapDISignatureTypeToReg(const DIType *Ty,
+                                                 MCRegister VoidTypeReg,
+                                                 bool ReturnType);
 
   /// Map a DWARF source language code to a NonSemantic.Shader.DebugInfo.100
   /// source language code.
@@ -681,20 +711,20 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// \c DIFile, or another scope without a dedicated debug instruction, falls
   /// back to \p FallbackCU or the first module \c DebugCompilationUnit recorded
   /// in \c DebugScopeRegs; no such compile unit is Unsupported.
-  EmitResult resolveScope(const DIScope *Scope,
-                          const DICompileUnit *FallbackCU = nullptr);
+  EmitResult<MCRegister>
+  resolveScope(const DIScope *Scope, const DICompileUnit *FallbackCU = nullptr);
 
-  /// Emit \c DebugLexicalBlock for \p S, which must be a \c DILexicalBlock or
+  /// Build \c DebugLexicalBlock for \p S, which must be a \c DILexicalBlock or
   /// a \c DINamespace. A \c DILexicalBlock supplies Line/Column
   /// from \c getLine()/getColumn(); a \c DINamespace has neither, so both are
-  /// emitted as 0, and its Name is appended as an extra \c OpString operand.
+  /// 0, and its Name is appended as an extra \c OpString operand.
   ///
-  /// \returns Emitted with the result id on success. If \c resolveScope cannot
-  /// emit \c S->getScope(), emits nothing and returns that status.
-  EmitResult emitDebugLexicalBlock(const DIScope *S, MCRegister VoidTypeReg,
-                                   MCRegister I32TypeReg,
-                                   MCRegister ExtInstSetReg,
-                                   SPIRV::ModuleAnalysisInfo &MAI);
+  /// \returns Emitted when the operands are ready. If \c resolveScope cannot
+  /// emit \c S->getScope(), builds nothing and returns that status.
+  EmitResult<PreparedScope>
+  emitDebugLexicalBlock(const DIScope *S, MCRegister VoidTypeReg,
+                        MCRegister I32TypeReg, MCRegister ExtInstSetReg,
+                        SPIRV::ModuleAnalysisInfo &MAI);
 
   /// Return a cached \c DebugInlinedAt id for \p IA, or emit one (recursing
   /// into \c IA->getInlinedAt() first for the optional Inlined operand, so



More information about the llvm-branch-commits mailing list