DebugFunction does not have an Function Id operand in NonSemantic.Shader debug info specification. It's been replaced by the whole new DebugFunctionDefinition instruction to avoid forward references.
This instruction must appear in the entry basic block of an OpFunction.
Specification:
https://github.com/KhronosGroup/SPIRV-Registry/blob/main/nonsemantic/NonSemantic.Shader.DebugInfo.100.asciidoc#DebugFunctionDefinition
Gbp-Pq: Name 0035-Backport-to-15-DebugInfo-Support-translation-of-Debu.patch
transformToConstant(Ops, {LineIdx, ColumnIdx, FlagsIdx});
SPIRVEntry *DebugFunc = nullptr;
+ SPIRVValue *FuncDef = nullptr;
if (!Func->isDefinition()) {
DebugFunc =
BM->addDebugInfo(SPIRVDebug::FunctionDeclaration, getVoidTy(), Ops);
SPIRVValue *SPIRVFunc = SPIRVWriter->getTranslatedValue(&F);
assert(SPIRVFunc && "All function must be already translated");
Ops[FunctionIdIdx] = SPIRVFunc->getId();
+ FuncDef = SPIRVFunc;
break;
}
}
+ // For NonSemantic.Shader.DebugInfo we store Function Id index as a
+ // separate DebugFunctionDefinition instruction.
+ if (isNonSemanticDebugInfo())
+ Ops.pop_back();
if (DISubprogram *FuncDecl = Func->getDeclaration())
Ops.push_back(transDbgEntry(FuncDecl)->getId());
if (DITemplateParameterArray TPA = Func->getTemplateParams()) {
DebugFunc = transDbgTemplateParams(TPA, DebugFunc);
}
+
+ if (isNonSemanticDebugInfo())
+ transDbgFuncDefinition(FuncDef, DebugFunc);
+
return DebugFunc;
}
+SPIRVEntry *LLVMToSPIRVDbgTran::transDbgFuncDefinition(SPIRVValue *FuncDef,
+ SPIRVEntry *DbgFunc) {
+ if (!isNonSemanticDebugInfo() || !FuncDef)
+ return nullptr;
+
+ using namespace SPIRVDebug::Operand::FunctionDefinition;
+ SPIRVWordVec Ops(OperandCount);
+ Ops[FunctionIdx] = DbgFunc->getId();
+ Ops[DefinitionIdx] = FuncDef->getId();
+ SPIRVFunction *F = static_cast<SPIRVFunction *>(FuncDef);
+ SPIRVBasicBlock *BB = F->getNumBasicBlock() ? F->getBasicBlock(0) : nullptr;
+ SPIRVId ExtSetId = BM->getExtInstSetId(BM->getDebugInfoEIS());
+
+ return BM->addExtInst(getVoidTy(), ExtSetId, SPIRVDebug::FunctionDefinition,
+ Ops, BB, BB->getInst(0));
+}
+
// Location information
SPIRVEntry *LLVMToSPIRVDbgTran::transDbgScope(const DIScope *S) {
SPIRVEntry *transDbgGlobalVariable(const DIGlobalVariable *GV);
SPIRVEntry *transDbgFunction(const DISubprogram *Func);
+ SPIRVEntry *transDbgFuncDefinition(SPIRVValue *SPVFunc, SPIRVEntry *DbgFunc);
+
// Location information
SPIRVEntry *transDbgScope(const DIScope *S);
SPIRVEntry *transDebugLoc(const DebugLoc &Loc, SPIRVBasicBlock *BB,
// Function declaration descriptor
DISubprogram *FD = nullptr;
- if (Ops.size() > DeclarationIdx) {
+ if (isNonSemanticDebugInfo(DebugInst->getExtSetKind()) &&
+ Ops.size() > DeclarationNonSemIdx) {
+ FD = transDebugInst<DISubprogram>(
+ BM->get<SPIRVExtInst>(Ops[DeclarationNonSemIdx]));
+ } else if (Ops.size() > DeclarationIdx) {
FD = transDebugInst<DISubprogram>(
BM->get<SPIRVExtInst>(Ops[DeclarationIdx]));
}
// Create targetFuncName mostly for Fortran trampoline function if it is
// the case
StringRef TargetFunction;
- if (Ops.size() > TargetFunctionNameIdx) {
+ if (Ops.size() > MinOperandCount) {
TargetFunction = getString(Ops[TargetFunctionNameIdx]);
}
DIS = getDIBuilder(DebugInst).createFunction(
/*Annotations*/ nullptr, TargetFunction);
}
DebugInstCache[DebugInst] = DIS;
- SPIRVId RealFuncId = Ops[FunctionIdIdx];
- FuncMap[RealFuncId] = DIS;
- // Function.
- SPIRVEntry *E = BM->getEntry(Ops[FunctionIdIdx]);
+ // At this point, we don't have info about the function definition for
+ // NonSemantic.Shader debug info. If function definition is present, it'll be
+ // translated later within the function scope.
+ // For "default" debug info we do translate function body here.
+ if (!isNonSemanticDebugInfo(DebugInst->getExtSetKind()))
+ transFunctionBody(DIS, Ops[FunctionIdIdx]);
+
+ return DIS;
+}
+
+void SPIRVToLLVMDbgTran::transFunctionBody(DISubprogram *DIS, SPIRVId FuncId) {
+ FuncMap[FuncId] = DIS;
+
+ SPIRVEntry *E = BM->getEntry(FuncId);
if (E->getOpCode() == OpFunction) {
SPIRVFunction *BF = static_cast<SPIRVFunction *>(E);
llvm::Function *F = SPIRVReader->transFunction(BF);
if (!F->hasMetadata("dbg"))
F->setMetadata("dbg", DIS);
}
- return DIS;
+}
+
+DINode *
+SPIRVToLLVMDbgTran::transFunctionDefinition(const SPIRVExtInst *DebugInst) {
+ using namespace SPIRVDebug::Operand::FunctionDefinition;
+ const SPIRVWordVec &Ops = DebugInst->getArguments();
+
+ SPIRVExtInst *Func = BM->get<SPIRVExtInst>(Ops[FunctionIdx]);
+ DISubprogram *LLVMFunc = cast<DISubprogram>(DebugInstCache[Func]);
+
+ transFunctionBody(LLVMFunc, Ops[DefinitionIdx]);
+ return nullptr;
}
DINode *SPIRVToLLVMDbgTran::transFunctionDecl(const SPIRVExtInst *DebugInst) {
case SPIRVDebug::FunctionDeclaration:
return transFunctionDecl(DebugInst);
+ case SPIRVDebug::FunctionDefinition:
+ return transFunctionDefinition(DebugInst);
+
case SPIRVDebug::GlobalVariable:
return transGlobalVariable(DebugInst);
switch (DebugInst->getExtOp()) {
case SPIRVDebug::Scope:
case SPIRVDebug::NoScope:
+ case SPIRVDebug::FunctionDefinition:
return nullptr;
case SPIRVDebug::Declare: {
using namespace SPIRVDebug::Operand::DebugDeclare;
DINode *transLexicalBlockDiscriminator(const SPIRVExtInst *DebugInst);
DINode *transFunction(const SPIRVExtInst *DebugInst);
+ DINode *transFunctionDefinition(const SPIRVExtInst *DebugInst);
+ void transFunctionBody(DISubprogram *DIS, SPIRVId FuncId);
DINode *transFunctionDecl(const SPIRVExtInst *DebugInst);
Source = 35,
ModuleINTEL = 36,
InstCount = 37,
+ FunctionDefinition = 101,
Module = 200,
TypeSubrange = 201,
TypeArrayDynamic = 202,
FlagsIdx = 7,
ScopeLineIdx = 8,
FunctionIdIdx = 9,
+ DeclarationNonSemIdx = 9,
DeclarationIdx = 10,
- TargetFunctionNameIdx = 11,
+ // Only for NonSemantic.Schader.DebugInfo.200
+ TargetFunctionNameIdx = 10,
MinOperandCount = 10
};
}
+namespace FunctionDefinition {
+enum {
+ FunctionIdx = 0,
+ DefinitionIdx = 1,
+ OperandCount = 2
+};
+}
+
namespace LexicalBlock {
enum {
SourceIdx = 0,
add(SPIRVDebug::Module, "DebugModule");
add(SPIRVDebug::Expression, "DebugExpression");
add(SPIRVDebug::Operation, "DebugOperation");
+ add(SPIRVDebug::FunctionDefinition, "DebugFunctionDefinition");
}
SPIRV_DEF_NAMEMAP(SPIRVDebugExtOpKind, SPIRVDebugExtOpMap)
O << SPIRVNL() << MI.AsmTargetVec << MI.AsmVec;
}
- O << SPIRVNL() << MI.DebugInstVec << SPIRVNL() << MI.FuncVec;
+ // At this point we know that FunctionDefinition could have been included both
+ // into DebugInstVec and into basick block of function from FuncVec.
+ // By spec we should only have this instruction to be present inside the
+ // function body, so removing it from the DebugInstVec to avoid duplication.
+ MI.DebugInstVec.erase(
+ std::remove_if(MI.DebugInstVec.begin(), MI.DebugInstVec.end(),
+ [](SPIRVExtInst *I) {
+ return I->getExtOp() == SPIRVDebug::FunctionDefinition;
+ }),
+ MI.DebugInstVec.end());
+
+ O << SPIRVNL() << MI.DebugInstVec << SPIRVNL()
+ << MI.FuncVec;
return O;
}
// - Parent operand of DebugFunction is DebugCompilationUnit, not an OpString,
// even if in LLVM IR it points to a DIFile instead of DICompileUnit.
-// RUN: %clang_cc1 %s -cl-std=clc++ -emit-llvm-bc -triple spir -debug-info-kind=line-tables-only -O0 -o - | llvm-spirv -o %t.spv
-// RUN: llvm-spirv %t.spv --spirv-debug-info-version=nonsemantic-shader-100 -to-text -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+// RUN: %clang_cc1 %s -cl-std=clc++ -emit-llvm-bc -triple spir -debug-info-kind=line-tables-only -O0 -o %t.bc
+// RUN: llvm-spirv %t.bc --spirv-debug-info-version=nonsemantic-shader-100 -o %t.spv
+// RUN: llvm-spirv %t.spv -to-text -o %t.spt
+// RUN: FileCheck %s --input-file %t.spt --check-prefix=CHECK-SPIRV
+
+// RUN: llvm-spirv %t.bc --spirv-debug-info-version=nonsemantic-shader-200 -o %t.spv
+// RUN: llvm-spirv %t.spv -to-text -o %t.spt
+// RUN: FileCheck %s --input-file %t.spt --check-prefix=CHECK-SPIRV
+
// RUN: llvm-spirv -r -emit-opaque-pointers %t.spv -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK-LLVM
float foo(int i) {
// CHECK-SPIRV: String [[foo:[0-9]+]] "foo"
// CHECK-SPIRV: String [[k:[0-9]+]] "k"
// CHECK-SPIRV: [[CU:[0-9]+]] {{[0-9]+}} DebugCompilationUnit
-// CHECK-SPIRV: DebugFunction [[foo]] {{.*}} [[CU]] {{.*}} [[foo_id:[0-9]+]] {{[0-9]+}} {{$}}
-// CHECK-SPIRV: DebugFunction [[k]] {{.*}} [[CU]] {{.*}} [[k_id:[0-9]+]] {{[0-9]+}} {{$}}
+// CHECK-SPIRV: [[#FuncFoo:]] [[#]] DebugFunction [[foo]] {{.*}} [[CU]]
+// CHECK-SPIRV: [[#FuncK:]] [[#]] DebugFunction [[k]] {{.*}} [[CU]]
+// CHECK-SPIRV-NOT: DebugFunctionDefinition
+
+// CHECK-SPIRV: Function {{[0-9]+}} [[#foo_id:]]
+// CHECK-SPIRV: DebugFunctionDefinition [[#FuncFoo]] [[#foo_id]]
+// CHECK-LLVM: define spir_func float @_Z3fooi(i32 %i) #{{[0-9]+}} !dbg ![[#foo_id:]] {
-// CHECK-SPIRV: Function {{[0-9]+}} [[foo_id]]
-// CHECK-LLVM: define spir_func float @_Z3fooi(i32 %i) #{{[0-9]+}} !dbg !{{[0-9]+}} {
+// CHECK-SPIRV: Function {{[0-9]+}} [[#k_id:]]
+// CHECK-SPIRV: DebugFunctionDefinition [[#FuncK]] [[#k_id]]
+// CHECK-LLVM: define spir_kernel void @k() #{{[0-9]+}} !dbg ![[#k_id:]]
-// CHECK-SPIRV: Function {{[0-9]+}} [[k_id]]
-// CHECK-LLVM: define spir_kernel void @k() #{{[0-9]+}} !dbg !{{[0-9]+}}
+// CHECK-LLVM: ![[#foo_id]] = distinct !DISubprogram(name: "foo"
+// CHECK-LLVM: ![[#k_id]] = distinct !DISubprogram(name: "k"
; CHECK-SPIRV-DAG: String [[#TargetFunc:]] "_Z3foov"
; CHECK-SPIRV-DAG: ExtInst [[#]] [[#DebugNone:]] [[#]] DebugInfoNone
-; CHECK-SPIRV-DAG: ExtInst [[#]] [[#]] [[#]] DebugFunction [[#Func]] [[#]] [[#]] [[#]] [[#]] [[#]] [[#]] [[#]] [[#]] [[#]] [[#DebugNone]] [[#TargetFunc]]
+; CHECK-SPIRV-DAG: ExtInst [[#]] [[#]] [[#]] DebugFunction [[#Func]] [[#]] [[#]] [[#]] [[#]] [[#]] [[#]] [[#]] [[#]] [[#DebugNone]] [[#TargetFunc]]
; CHECK-LLVM: define spir_func void @_Z11foo_wrapperv() {{.*}} !dbg ![[#DbgSubProg:]] {
; CHECK-LLVM: ![[#DbgSubProg]] = distinct !DISubprogram(name: "foo_wrapper", linkageName: "_Z11foo_wrapperv", scope: null, file: ![[#]], line: 3, type: ![[#]], scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: ![[#]], templateParams: ![[#]], retainedNodes: ![[#]], targetFuncName: "_Z3foov")
; CHECK-SPIRV-200-DAG: ExtInst [[#]] [[#]] [[#Import]] DebugLocalVariable [[#]] [[#Type]]
; CHECK-SPIRV-200-DAG: ExtInst [[#]] [[#]] [[#Import]] DebugLocalVariable [[#]] [[#Type]]
-; CHECK-LLVM-200: ![[#]] = !DILocalVariable(name: "a", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type:]])
-; CHECK-LLVM-200: ![[#Type]] = !DIBasicType(name: "COMPLEX*8", size: 64, encoding: DW_ATE_complex_float)
-; CHECK-LLVM-200: ![[#]] = !DILocalVariable(name: "b", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type]])
-; CHECK-LLVM-200: ![[#]] = !DILocalVariable(name: "c", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type]])
+; CHECK-LLVM-200-DAG: ![[#]] = !DILocalVariable(name: "a", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type:]])
+; CHECK-LLVM-200-DAG: ![[#Type]] = !DIBasicType(name: "COMPLEX*8", size: 64, encoding: DW_ATE_complex_float)
+; CHECK-LLVM-200-DAG: ![[#]] = !DILocalVariable(name: "b", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type]])
+; CHECK-LLVM-200-DAG: ![[#]] = !DILocalVariable(name: "c", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type]])
; CHECK-SPIRV-100-DAG: ExtInstImport [[#Import:]] "NonSemantic.Shader.DebugInfo.100
; CHECK-SPIRV-100-DAG: String [[#Name:]] "COMPLEX*8"
; CHECK-SPIRV-100-DAG: ExtInst [[#]] [[#]] [[#Import]] DebugLocalVariable [[#]] [[#Type]]
; CHECK-SPIRV-100-DAG: ExtInst [[#]] [[#]] [[#Import]] DebugLocalVariable [[#]] [[#Type]]
-; CHECK-LLVM-100: ![[#]] = !DILocalVariable(name: "a", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type:]])
-; CHECK-LLVM-100: ![[#Type]] = !DIBasicType(tag: DW_TAG_unspecified_type, name: "COMPLEX*8")
-; CHECK-LLVM-100: ![[#]] = !DILocalVariable(name: "b", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type]])
-; CHECK-LLVM-100: ![[#]] = !DILocalVariable(name: "c", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type]])
+; CHECK-LLVM-100-DAG: ![[#]] = !DILocalVariable(name: "a", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type:]])
+; CHECK-LLVM-100-DAG: ![[#Type]] = !DIBasicType(tag: DW_TAG_unspecified_type, name: "COMPLEX*8")
+; CHECK-LLVM-100-DAG: ![[#]] = !DILocalVariable(name: "b", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type]])
+; CHECK-LLVM-100-DAG: ![[#]] = !DILocalVariable(name: "c", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#Type]])
; ModuleID = 'test.f90'
source_filename = "test.f90"