llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-spir-v

Author: Manuel Carrasco (mgcarrasco)

<details>
<summary>Changes</summary>

This PR brings support for cycles when processing DIScope(s). This is done 
using `SPV_KHR_relaxed_extended_instruction` for `OpExtInstWithForwardRefsKHR`, 
which permits forward references (a use prior to its definition).

The new emission algorithm works in the following way. DIScopes are recursively 
processed as it was implemented at the start of this stacked PRs. 
`getOrCreateDebugScope` reserves an id for the current scope, and recurses to 
first emit its dependencies. If in the recursion we hit a back edge (cycle), 
the scope even though not emitted yet will have a reserved id. That undefined 
but reserved id is then used as an operand alongside 
`OpExtInstWithForwardRefsKHR`.

When an instruction defining a DIScope is going to be emitted, 
`getOrCreateDebugScope` makes sure that the scope's instruction uses its 
reserved id. If the current scope is not supported by the NSDI support in the 
backend, it might be the case that the scope was still used as a forward 
reference. This forward reference, which has been emitted, must still be closed 
to remain valid. `getOrCreateDebugScope` keeps track of whether the scope has 
been used in a forward reference, and if unsupported, it defines it with a 
fresh DebugInfoNone. This might in some instances trigger invalid SPIRV due to 
conflicts with the spec or bugs in `spirv-val`. 

It is worth noting that ilegal SPIRV due to DebugInfoNone will only occur if 
the BE hits an unsupported feature, and the actual "bug" is the missing 
feature. A design where a scope chain is only emitted if all of it is supported 
would be much complex. If this is not acceptable by a consumer, this forward 
reference emission can be disabled via a flag. The BE has no support for cycles 
so it won't be a regression. This PR also provides foundations for a more 
complex design if needed.

As reference, the SPIRV-LLVM Translator already reserves ids for these cycles, 
but it does not use `SPV_KHR_relaxed_extended_instruction`, so the SPIRV is 
always invalid. Its SPIRV to LLVM path will also need to handle that extension 
for debug info in order to work with this implementation.

---

Patch is 29.29 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/229490.diff


10 Files Affected:

- (modified) llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp (+2-1) 
- (modified) llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp (+3-1) 
- (modified) llvm/lib/Target/SPIRV/SPIRVInstrInfo.td (+5) 
- (modified) llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp (+101-13) 
- (modified) llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h (+27-6) 
- (modified) llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td (+2) 
- (added) 
llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll 
(+57) 
- (added) 
llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer.ll (+56) 
- (added) 
llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-base-debuginfonone.ll (+53) 
- (added) 
llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-cycle-unsupported-forward-ref.ll
 (+43) 


``````````diff
diff --git a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp 
b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
index 8a2618b5309a9..3d78ac036caa6 100644
--- a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
@@ -176,7 +176,8 @@ void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t 
Address,
     printOpDecorate(MI, OS);
   } else if (OpCode == SPIRV::OpExtInstImport) {
     recordOpExtInstImport(MI);
-  } else if (OpCode == SPIRV::OpExtInst) {
+  } else if (OpCode == SPIRV::OpExtInst ||
+             OpCode == SPIRV::OpExtInstWithForwardRefsKHR) {
     printOpExtInst(MI, OS);
   } else if (OpCode == SPIRV::UNKNOWN_type) {
     printUnknownType(MI, OS);
diff --git a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp 
b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
index 956396355140c..0a61506de6e95 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
@@ -178,7 +178,9 @@ static const StringMap<SPIRV::Extension::Extension> 
SPIRVExtensionMap = {
      SPIRV::Extension::Extension::SPV_KHR_poison_freeze},
     {"SPV_KHR_untyped_pointers",
      SPIRV::Extension::Extension::SPV_KHR_untyped_pointers},
-    {"SPV_EXT_long_vector", SPIRV::Extension::Extension::SPV_EXT_long_vector}};
+    {"SPV_EXT_long_vector", SPIRV::Extension::Extension::SPV_EXT_long_vector},
+    {"SPV_KHR_relaxed_extended_instruction",
+     SPIRV::Extension::Extension::SPV_KHR_relaxed_extended_instruction}};
 
 bool SPIRVExtensionsParser::parse(cl::Option &O, StringRef ArgName,
                                   StringRef ArgValue, ExtensionSet &Vals) {
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td 
b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
index cd4faf80c2d97..aa4c0ed1cea28 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
@@ -152,6 +152,11 @@ def OpExtInstImport: Op<11, (outs ID:$res), (ins 
StringImm:$extInstsName, variab
 // perspective of the instruction selection pass
 def OpExtInst: Op<12, (outs ID:$res), (ins TYPE:$ty, i32imm:$set, 
Extension:$inst, variable_ops),
                   "$res = OpExtInst $ty $set $inst">;
+// Same layout as OpExtInst; allows operands that are defined later in the
+// module (SPV_KHR_relaxed_extended_instruction).
+def OpExtInstWithForwardRefsKHR: Op<4433, (outs ID:$res),
+                  (ins TYPE:$ty, i32imm:$set, Extension:$inst, variable_ops),
+                  "$res = OpExtInstWithForwardRefsKHR $ty $set $inst">;
 // 3.42.5 Mode-Setting Instructions
 
 def OpMemoryModel: Op<14, (outs), (ins AddressingModel:$addr, 
MemoryModel:$mem),
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp 
b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index b741113c6540c..a1e5a1964f2ce 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -32,6 +32,7 @@
 #include "llvm/IR/Module.h"
 #include "llvm/MC/MCInst.h"
 #include "llvm/MC/MCStreamer.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/Path.h"
@@ -39,6 +40,12 @@
 
 using namespace llvm;
 
+static cl::opt<bool> SPIRVDebugScopeForwardRefs(
+    "spirv-debug-scope-forward-refs",
+    cl::desc("Forward-reference debug scopes when "
+             "SPV_KHR_relaxed_extended_instruction is enabled"),
+    cl::init(false));
+
 namespace {
 
 /// Look up \p Key in a register map and return its value, or std::nullopt when
@@ -340,6 +347,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   LexicalBlocks.clear();
   DebugScopeRegs.clear();
   ScopesInProgress.clear();
+  HasRelaxedExtInst = false;
   FailedScopes.clear();
   DebugInlinedAtRegs.clear();
   ScopeToPathOpStringReg.clear();
@@ -453,6 +461,14 @@ void SPIRVNonSemanticDebugHandler::prepareModuleOutput(
   // Add the extension to requirements so OpExtension is output.
   MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info);
 
+  HasRelaxedExtInst =
+      SPIRVDebugScopeForwardRefs &&
+      ST.canUseExtension(
+          SPIRV::Extension::SPV_KHR_relaxed_extended_instruction);
+  if (HasRelaxedExtInst)
+    MAI.Reqs.addExtension(
+        SPIRV::Extension::SPV_KHR_relaxed_extended_instruction);
+
   // Add the NonSemantic.Shader.DebugInfo.100 entry to ExtInstSetMap so that
   // outputOpExtInstImports() emits the OpExtInstImport instruction. Allocate a
   // fresh result ID for it now; the same ID is used in emitExtInst() operands.
@@ -537,14 +553,39 @@ MCRegister 
SPIRVNonSemanticDebugHandler::emitOpConstantI32(
                             /*Width=*/32, MAI);
 }
 
+bool SPIRVNonSemanticDebugHandler::markForwardReferencedOperands(
+    ArrayRef<MCRegister> Operands) {
+  bool HasForwardRef = false;
+  for (auto &Entry : ScopesInProgress) {
+    InProgressScope &InProgress = Entry.second;
+    assert(InProgress.Id.isValid() &&
+           "in-progress scope must have a reserved id");
+    if (!is_contained(Operands, InProgress.Id))
+      continue;
+    InProgress.ForwardReferenced = true;
+    HasForwardRef = true;
+  }
+  return HasForwardRef;
+}
+
 MCRegister SPIRVNonSemanticDebugHandler::emitExtInst(
     SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode,
     MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
-    ArrayRef<MCRegister> Operands, SPIRV::ModuleAnalysisInfo &MAI) {
-  MCRegister Reg = MAI.getNextIDRegister();
+    ArrayRef<MCRegister> Operands, SPIRV::ModuleAnalysisInfo &MAI,
+    std::optional<MCRegister> ResultReg) {
+  assert((!ResultReg || ResultReg->isValid()) &&
+         "ResultReg must be a valid id when provided");
+  MCRegister Reg = ResultReg ? *ResultReg : MAI.getNextIDRegister();
+
+  // An operand that is still an in-progress scope id is a forward reference.
+  // Tag the instruction with OpExtInstWithForwardRefsKHR and remember the id:
+  // if that scope later fails, the id still needs a definition.
+  bool HasForwardRef =
+      HasRelaxedExtInst && markForwardReferencedOperands(Operands);
 
   MCInst Inst;
-  Inst.setOpcode(SPIRV::OpExtInst);
+  Inst.setOpcode(HasForwardRef ? SPIRV::OpExtInstWithForwardRefsKHR
+                               : SPIRV::OpExtInst);
   Inst.addOperand(MCOperand::createReg(Reg));
   Inst.addOperand(MCOperand::createReg(VoidTypeReg));
   Inst.addOperand(MCOperand::createReg(ExtInstSetReg));
@@ -760,8 +801,11 @@ 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.
+// node L emits f, then L. Entering a scope reserves its result id. For
+// `struct S { S *p; }`, S* is a back edge. When forward references are
+// enabled, that id is used with OpExtInstWithForwardRefsKHR, and a later
+// failure defines it as DebugInfoNone only when an emitted instruction already
+// named it. Otherwise the edge is dropped and the reserved id stays unused.
 auto SPIRVNonSemanticDebugHandler::getOrCreateDebugScope(const DIScope *S)
     -> EmitResult<MCRegister> {
   if (!S || FailedScopes.contains(S))
@@ -769,16 +813,55 @@ auto 
SPIRVNonSemanticDebugHandler::getOrCreateDebugScope(const DIScope *S)
   if (auto Reg = lookupOptReg(DebugScopeRegs, S))
     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<MCRegister>::inProgress();
+  // Mark the scope node as in progress while its dependencies are
+  // resolved/emitted.
+  auto [InProgressIt, FirstVisit] = ScopesInProgress.try_emplace(S);
+  if (!FirstVisit) { // We hit a back edge, it is not the first insertion.
+    if (!HasRelaxedExtInst)
+      return EmitResult<MCRegister>::inProgress(); // No support for forward
+                                                   // refs.
+    // Use the reserved id for the back edge when forward references are
+    // supported.
+    MCRegister ReservedId = InProgressIt->second.Id;
+    assert(ReservedId.isValid() && "in-progress scope must have a reserved 
id");
+    return EmitResult<MCRegister>::emitted(ReservedId);
+  }
+
+  // Reserve the result id first: a dependency may refer back to this scope.
+  assert(CurrentMAI && "debug scope id allocation requires CurrentMAI");
+  MCRegister Id = CurrentMAI->getNextIDRegister();
+  assert(Id.isValid() && "debug scope id must be a valid register");
+  InProgressIt->second.Id = Id;
 
   EmitResult<PreparedScope> P = emitDebugScope(S);
-  ScopesInProgress.erase(S);
-  assert((!P || !GlobalNSDIEnabled) &&
-         "debug type or scope created after module-scope NSDI emission");
+  assert((!HasRelaxedExtInst || P.Status == EmitStatus::Emitted ||
+          P.Status == EmitStatus::Unsupported) &&
+         "InProgress is only returned when forward references are 
unsupported");
   if (!P) {
+    // Emission during recursion update the container. Look the scope up again.
+    InProgressIt = ScopesInProgress.find(S);
+    assert(InProgressIt != ScopesInProgress.end() &&
+           "scope must stay in progress until its instruction is emitted");
+    bool ForwardReferenced = InProgressIt->second.ForwardReferenced;
+    ScopesInProgress.erase(InProgressIt);
+    // P here is either unsupported or in progress. When forward references are
+    // supported, P can only be unsupported. Define the id only when an
+    // already-emitted instruction named it.
+    if (ForwardReferenced) {
+      assert(HasRelaxedExtInst &&
+             "a forward reference requires relaxed extended instructions");
+      assert(P.Status == EmitStatus::Unsupported &&
+             "a forward-referenced scope fails as unsupported");
+      SPIRV::ModuleAnalysisInfo &MAI = *CurrentMAI;
+      emitExtInst(SPIRV::NonSemanticExtInst::DebugInfoNone,
+                  getOrEmitOpTypeVoidReg(MAI), MAI.getExtInstSetReg(NSSet), {},
+                  MAI, Id);
+    }
+
+    // InProgress is a dropped back edge (when forward references are not
+    // supported). Leave it out of FailedScopes so a later walk can retry the
+    // scope. Regardless of forward references, cache unsupported scopes to
+    // avoid retrying them.
     if (P.Status == EmitStatus::Unsupported)
       FailedScopes.insert(S);
     return P.Status == EmitStatus::InProgress
@@ -790,7 +873,12 @@ auto 
SPIRVNonSemanticDebugHandler::getOrCreateDebugScope(const DIScope *S)
   SPIRV::ModuleAnalysisInfo &MAI = *CurrentMAI;
   MCRegister Reg =
       emitExtInst(P.Value->Opcode, getOrEmitOpTypeVoidReg(MAI),
-                  MAI.getExtInstSetReg(NSSet), P.Value->Operands, MAI);
+                  MAI.getExtInstSetReg(NSSet), P.Value->Operands, MAI, Id);
+  assert(Reg == Id &&
+         "debug scope result id must reuse the id reserved at entry");
+  ScopesInProgress.erase(S);
+  assert(!GlobalNSDIEnabled &&
+         "debug type or scope created after module-scope NSDI emission");
   DebugScopeRegs[S] = Reg;
   return EmitResult<MCRegister>::emitted(Reg);
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h 
b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 8e17d8cf0d39b..e0f351238bace 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -133,8 +133,20 @@ class SPIRVNonSemanticDebugHandler : public 
DebugHandlerBase {
     }
   };
 
+  // Used to track which scopes are in progress, and which operands are used by
+  // a forward references. Operands must be later defined even if the scope
+  // fails to emit.
+  struct InProgressScope {
+    MCRegister Id;
+    bool ForwardReferenced = false;
+  };
+
   // Types and scopes whose instruction is currently being built.
-  SmallPtrSet<const DIScope *, 8> ScopesInProgress;
+  SmallDenseMap<const DIScope *, InProgressScope, 8> ScopesInProgress;
+
+  // True when -spirv-debug-scope-forward-refs is set and prepareModuleOutput
+  // saw SPV_KHR_relaxed_extended_instruction.
+  bool HasRelaxedExtInst = false;
 
   // Types and scopes that are not supported.
   SmallPtrSet<const DIScope *, 8> FailedScopes;
@@ -386,10 +398,16 @@ class SPIRVNonSemanticDebugHandler : public 
DebugHandlerBase {
                                 bool UseEmptyPathIfNullScope = false);
   MCRegister emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg,
                                SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Mark in-progress scopes named by \p Operands. Return true when any
+  /// operand is such an id. A marked id needs a definition if that scope 
fails.
+  bool markForwardReferencedOperands(ArrayRef<MCRegister> Operands);
+
   MCRegister emitExtInst(SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode,
                          MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
                          ArrayRef<MCRegister> Operands,
-                         SPIRV::ModuleAnalysisInfo &MAI);
+                         SPIRV::ModuleAnalysisInfo &MAI,
+                         std::optional<MCRegister> ResultReg = std::nullopt);
 
   /// Return OpTypeVoid id for this module (lazy lookup / emit, then cache).
   MCRegister getOrEmitOpTypeVoidReg(SPIRV::ModuleAnalysisInfo &MAI);
@@ -407,10 +425,13 @@ class SPIRVNonSemanticDebugHandler : public 
DebugHandlerBase {
 
   /// Return the id for the type or scope \p S, emitting it and the types and
   /// scopes it names first. One already in \c DebugScopeRegs is Emitted. A 
null
-  /// \p S or one in \c FailedScopes is Unsupported. One already in
-  /// \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.
+  /// \p S or one in \c FailedScopes is Unsupported. Entering a scope reserves
+  /// its result id. A back edge (\p S already in \c ScopesInProgress) returns
+  /// that id when \c HasRelaxedExtInst is set, and InProgress otherwise. If
+  /// emission fails after an emitted instruction used that id, it is defined 
as
+  /// DebugInfoNone. An id that nothing named stays undefined. An Unsupported
+  /// result is recorded in \c FailedScopes; an InProgress one is not, so a
+  /// later walk can retry it.
   EmitResult<MCRegister> getOrCreateDebugScope(const DIScope *S);
 
   /// Build the instruction for \p S. Only \c getOrCreateDebugScope calls this
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td 
b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index 561d0fdef4026..a0604b451cd12 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -406,6 +406,8 @@ defm SPV_KHR_abort : ExtensionOperand<138, [EnvOpenCL]>;
 defm SPV_KHR_poison_freeze : ExtensionOperand<139, [EnvVulkan, EnvOpenCL]>;
 defm SPV_KHR_untyped_pointers : ExtensionOperand<140, [EnvOpenCL]>;
 defm SPV_EXT_long_vector : ExtensionOperand<141, [EnvOpenCL, EnvVulkan]>;
+defm SPV_KHR_relaxed_extended_instruction
+    : ExtensionOperand<142, [EnvVulkan, EnvOpenCL]>;
 
 
//===----------------------------------------------------------------------===//
 // Multiclass used to define Capabilities enum values and at the same time
diff --git 
a/llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll 
b/llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll
new file mode 100644
index 0000000000000..c6bb31bac1d00
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll
@@ -0,0 +1,57 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 
-mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s 
--check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs 
--spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s 
-o - -filetype=obj | spirv-val %}
+; RUN: llc --verify-machineinstrs 
--spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction 
--spirv-debug-scope-forward-refs=true -O0 -mtriple=spirv64-unknown-unknown %s 
-o - | FileCheck %s --check-prefix=FWD
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs 
--spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction 
--spirv-debug-scope-forward-refs=true -O0 -mtriple=spirv64-unknown-unknown %s 
-o - -filetype=obj | not spirv-val 2>&1 | FileCheck %s --check-prefix=VAL %}
+; RUN: llc --verify-machineinstrs 
--spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction 
--spirv-debug-scope-forward-refs=0 -O0 -mtriple=spirv64-unknown-unknown %s -o - 
| FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs 
--spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction 
--spirv-debug-scope-forward-refs=0 -O0 -mtriple=spirv64-unknown-unknown %s -o - 
-filetype=obj | spirv-val %}
+
+; struct S { int x; } is declared in namespace ns, and ns is declared in f.
+; f is scoped in an unsupported enumeration.
+;
+; Namespaces are emitted before types, so ns is entered first and enters f
+; while ns is still open. f's parameter type is S*, and S's Parent is ns, so
+; S is emitted with ns's reserved id. f then fails, ns fails, and that id is
+; defined as DebugInfoNone. -spirv-debug-scope-forward-refs defaults to off,
+; so without that flag the back edge is dropped and S is not emitted.
+;
+; NonSemantic.Shader.DebugInfo says DebugTypeComposite's Parent must be a
+; DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or
+; DebugTypeComposite. DebugInfoNone is not one of those. spirv-val rejects
+; it, and that matches the spec.
+
+; CHECK-SPIRV-NOT: DebugTypeComposite
+; CHECK-SPIRV-NOT: OpExtInstWithForwardRefsKHR
+
+; FWD: OpExtension "SPV_KHR_relaxed_extended_instruction"
+; FWD: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; FWD-DAG: [[void:%[0-9]+]] = OpTypeVoid
+; FWD-DAG: [[name_s:%[0-9]+]] = OpString "S"
+; FWD: [[s:%[0-9]+]] = OpExtInstWithForwardRefsKHR [[void]] [[ext]] 
DebugTypeComposite [[name_s]] {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} 
[[parent:%[0-9]+]]
+; FWD: [[parent]] = OpExtInst [[void]] [[ext]] DebugInfoNone
+
+; VAL: DebugTypeComposite: expected operand Parent must be a result id of a 
lexical scope
+
+define spir_func void @f(ptr addrspace(4) %p) !dbg !6 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!1, !2}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, 
producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: 
FullDebug)
+!1 = !{i32 7, !"Dwarf Version", i32 5}
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = !DIFile(filename: "ns-parent.cpp", directory: "/src")
+
+!6 = distinct !DISubprogram(name: "f", scope: !7, file: !3, line: 1, type: !8, 
scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!7 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "E", file: !3, line: 
1, size: 32, elements: !12)
+!8 = !DISubroutineType(types: !11)
+!11 = !{null, !14}
+!12 = !{}
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!13 = !DINamespace(name: "ns", scope: !6)
+!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S", scope: 
!13, file: !3, line: 2, size: 32, elements: !15)
+!15 = !{!16}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "x", file: !3, line: 2, 
baseType: !9, size: 32)
+!14 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64, 
dwarfAddressSpace: 4)
diff --git 
a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer.ll 
b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer.ll
new file mode 100644
index 0000000000000..2e4e4402e5de6
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer.ll
@@ -0,0 +1,56 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 
-mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s 
--check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs 
--spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s 
-o - -filetype=obj | spirv-val %}
+; RUN: llc --verify-machineinstrs 
--spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction 
--spirv-debug-scope-forward-...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/229490
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to