Skip to content

Commit 1fd886c

Browse files
MiloszSkobejkoigcbot
authored andcommitted
Implement DIGlobalVariable handling in DWARF part 1
Partially Implemented global variable handling in DWARF. Now global variables with type `DIGlobalVariable` and in global addrspace can be visible in debugger. This commit skips emmiting DW_AT_Location and DW_OP_addr.
1 parent 6302654 commit 1fd886c

File tree

4 files changed

+191
-8
lines changed

4 files changed

+191
-8
lines changed

IGC/DebugInfo/DwarfCompileUnit.cpp

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ void CompileUnit::addBlock(DIE *Die, dwarf::Attribute Attribute, IGC::DIEBlock *
386386
/// entry.
387387
void CompileUnit::addSourceLine(DIE *Die, DIScope *S, unsigned Line) {
388388
// If the line number is 0, don't add it.
389-
if (Line == 0)
389+
if (Line == 0 || !S)
390390
return;
391391

392392
unsigned FileID = DD->getOrCreateSourceID(S->getFilename(), S->getDirectory(), DD->getMD5AsBytes(S->getFile()),
@@ -444,6 +444,16 @@ void CompileUnit::addSourceLine(DIE *Die, DIType *Ty) {
444444
addSourceLine(Die, Ty, Ty->getLine());
445445
}
446446

447+
/// addSourceLine - Add location information to specified debug information
448+
/// entry.
449+
void CompileUnit::addSourceLine(DIE *Die, DIGlobalVariable *GV) {
450+
// Verify type.
451+
if (!isa<DIGlobalVariable>(GV))
452+
return;
453+
454+
addSourceLine(Die, GV->getScope(), GV->getLine());
455+
}
456+
447457
void CompileUnit::addRegOrConst(IGC::DIEBlock *TheDie, unsigned DWReg) {
448458
// TODO: Confirm if this function is correctly used.
449459
auto DWRegEncoded = GetEncodedRegNum<RegisterNumbering::GRFBase>(DWReg);
@@ -1505,10 +1515,6 @@ void CompileUnit::constructTemplateValueParameterDIE(DIE &Buffer, DITemplateValu
15051515
/// constructImportedEntityDIE - Create a DIE for DIImportedEntity.
15061516
IGC::DIE *CompileUnit::constructImportedEntityDIE(DIImportedEntity *Module) {
15071517
DINode *Entity = Module->getEntity();
1508-
if (auto *GV = dyn_cast<DIGlobalVariable>(Entity))
1509-
return nullptr; // Missing support for imported entity linked to a global
1510-
// variable
1511-
15121518
DIE *IMDie = new DIE(Module->getTag());
15131519
insertDIE(Module, IMDie);
15141520

@@ -1521,9 +1527,8 @@ IGC::DIE *CompileUnit::constructImportedEntityDIE(DIImportedEntity *Module) {
15211527
EntityDie = getOrCreateSubprogramDIE(SP);
15221528
else if (auto *T = dyn_cast<DIType>(Entity))
15231529
EntityDie = getOrCreateTypeDIE(T);
1524-
// else if (auto* GV = dyn_cast<DIGlobalVariable>(Entity)) // TODO missing
1525-
// support
1526-
// EntityDie = getOrCreateGlobalVariableDIE(GV, {});
1530+
else if (auto *GV = dyn_cast<DIGlobalVariable>(Entity))
1531+
EntityDie = getOrCreateGlobalVariableDIE(GV, {});
15271532
else
15281533
EntityDie = getDIE(Entity);
15291534

@@ -1556,6 +1561,59 @@ IGC::DIE *CompileUnit::getOrCreateNameSpace(DINamespace *NS) {
15561561
return NDie;
15571562
}
15581563

1564+
IGC::DIE *CompileUnit::getOrCreateGlobalVariableDIE(DIGlobalVariable *GV, ArrayRef<GlobalExpr> GlobalExprs) {
1565+
// Check for pre-existence.
1566+
if (DIE *Die = getDIE(GV))
1567+
return Die;
1568+
1569+
auto *GVContext = GV->getScope();
1570+
DIType *GTy = GV->getType();
1571+
1572+
DIE *ContextDIE = getOrCreateContextDIE(GVContext);
1573+
1574+
// Add to map.
1575+
DIE *VariableDIE = createAndAddDIE(GV->getTag(), *ContextDIE, GV);
1576+
DIScope *DeclContext;
1577+
if (auto *SDMDecl = GV->getStaticDataMemberDeclaration()) {
1578+
DeclContext = SDMDecl->getScope();
1579+
assert(SDMDecl->isStaticMember() && "Expected static member decl");
1580+
assert(GV->isDefinition());
1581+
// We need the declaration DIE that is in the static member's class.
1582+
DIE *VariableSpecDIE = getOrCreateStaticMemberDIE(SDMDecl);
1583+
addDIEEntry(VariableDIE, dwarf::DW_AT_specification, VariableSpecDIE);
1584+
// If the global variable's type is different from the one in the class
1585+
// member type, assume that it's more specific and also emit it.
1586+
if (GTy != SDMDecl->getBaseType())
1587+
addType(VariableDIE, GTy);
1588+
} else {
1589+
DeclContext = GV->getScope();
1590+
// Add name and type.
1591+
addString(VariableDIE, dwarf::DW_AT_name, GV->getDisplayName());
1592+
if (GTy)
1593+
addType(VariableDIE, GTy);
1594+
1595+
// Add scoping info.
1596+
if (!GV->isLocalToUnit())
1597+
addFlag(VariableDIE, dwarf::DW_AT_external);
1598+
1599+
// Add line number info.
1600+
addSourceLine(VariableDIE, GV);
1601+
}
1602+
1603+
if (!GV->isDefinition())
1604+
addFlag(VariableDIE, dwarf::DW_AT_declaration);
1605+
1606+
if (uint32_t AlignInBytes = GV->getAlignInBytes())
1607+
addUInt(VariableDIE, dwarf::DW_AT_alignment, dwarf::DW_FORM_udata, AlignInBytes);
1608+
1609+
if (MDTuple *TP = GV->getTemplateParams())
1610+
addTemplateParams(*VariableDIE, DINodeArray(TP));
1611+
1612+
// TODO: Add location and relocation entry to Global Variable
1613+
// addLocationAttribute(VariableDIE, GV, GlobalExprs);
1614+
return VariableDIE;
1615+
}
1616+
15591617
/// getOrCreateSubprogramDIE - Create new DIE using SP.
15601618
IGC::DIE *CompileUnit::getOrCreateSubprogramDIE(DISubprogram *SP) {
15611619
// Construct the context before querying for the existence of the DIE in case

IGC/DebugInfo/DwarfCompileUnit.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ See LICENSE.TXT for details.
2424
#include "llvm/ADT/DenseMap.h"
2525
#include <llvmWrapper/ADT/Optional.h>
2626
#include "llvm/ADT/SmallVector.h"
27+
#include <llvm/ADT/ArrayRef.h>
2728
#include "llvm/ADT/StringMap.h"
2829
#include "llvm/ADT/StringRef.h"
2930
#include "llvm/Config/llvm-config.h"
@@ -161,6 +162,12 @@ class CompileUnit {
161162

162163
DIEBlock *getDIEBlock() { return new (DIEValueAllocator) DIEBlock(); }
163164

165+
/// A pair of GlobalVariable and DIExpression.
166+
struct GlobalExpr {
167+
const llvm::GlobalVariable *Var;
168+
const llvm::DIExpression *Expr;
169+
};
170+
164171
/// insertDIE - Insert DIE into the map. We delegate the request to DwarfDebug
165172
/// when the llvm::MDNode can be part of the type system, since DIEs for
166173
/// the type system can be shared across CUs and the mappings are
@@ -240,6 +247,7 @@ class CompileUnit {
240247
void addSourceLine(DIE *Die, llvm::DIVariable *V);
241248
void addSourceLine(DIE *Die, llvm::DISubprogram *SP);
242249
void addSourceLine(DIE *Die, llvm::DIType *Ty);
250+
void addSourceLine(DIE *Die, llvm::DIGlobalVariable *GV);
243251

244252
/// addConstantValue - Add constant value entry in variable DIE.
245253
void addConstantValue(DIE *Die, const llvm::ConstantInt *CI, bool Unsigned);
@@ -325,6 +333,9 @@ class CompileUnit {
325333
/// getOrCreateNameSpace - Create a DIE for DINameSpace.
326334
DIE *getOrCreateNameSpace(llvm::DINamespace *NS);
327335

336+
/// Get or create global variable DIE.
337+
DIE *getOrCreateGlobalVariableDIE(llvm::DIGlobalVariable *GV, llvm::ArrayRef<GlobalExpr> GlobalExprs);
338+
328339
/// getOrCreateSubprogramDIE - Create new DIE using SP.
329340
DIE *getOrCreateSubprogramDIE(llvm::DISubprogram *SP);
330341

IGC/DebugInfo/DwarfDebug.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,28 @@ IGCLLVM::optional<llvm::MD5::MD5Result> DwarfDebug::getMD5AsBytes(const llvm::DI
10681068
return CKMem;
10691069
}
10701070

1071+
/// Sort and unique GVEs by comparing their fragment offset.
1072+
static SmallVectorImpl<CompileUnit::GlobalExpr> &sortGlobalExprs(SmallVectorImpl<CompileUnit::GlobalExpr> &GVEs) {
1073+
llvm::sort(GVEs, [](CompileUnit::GlobalExpr A, CompileUnit::GlobalExpr B) {
1074+
// Sort order: first null exprs, then exprs without fragment
1075+
// info, then sort by fragment offset in bits.
1076+
// FIXME: Come up with a more comprehensive comparator so
1077+
// the sorting isn't non-deterministic, and so the following
1078+
// std::unique call works correctly.
1079+
if (!A.Expr || !B.Expr)
1080+
return !!B.Expr;
1081+
auto FragmentA = A.Expr->getFragmentInfo();
1082+
auto FragmentB = B.Expr->getFragmentInfo();
1083+
if (!FragmentA || !FragmentB)
1084+
return !!FragmentB;
1085+
return FragmentA->OffsetInBits < FragmentB->OffsetInBits;
1086+
});
1087+
GVEs.erase(std::unique(GVEs.begin(), GVEs.end(),
1088+
[](CompileUnit::GlobalExpr A, CompileUnit::GlobalExpr B) { return A.Expr == B.Expr; }),
1089+
GVEs.end());
1090+
return GVEs;
1091+
}
1092+
10711093
// Emit all Dwarf sections that should come prior to the content. Create
10721094
// global DIEs and emit initial debug info sections.
10731095
void DwarfDebug::beginModule() {
@@ -1077,6 +1099,14 @@ void DwarfDebug::beginModule() {
10771099
if (M->debug_compile_units().begin() == M->debug_compile_units().end())
10781100
return;
10791101

1102+
DenseMap<DIGlobalVariable *, SmallVector<CompileUnit::GlobalExpr, 1>> GVMap;
1103+
for (const GlobalVariable &Global : M->globals()) {
1104+
SmallVector<DIGlobalVariableExpression *, 1> GVs;
1105+
Global.getDebugInfo(GVs);
1106+
for (auto *GVE : GVs)
1107+
GVMap[GVE->getVariable()].push_back({&Global, GVE->getExpression()});
1108+
}
1109+
10801110
// discover DISubprogramNodes for all the registered visaModules
10811111
discoverDISPNodes();
10821112
// Emit initial sections so we can reference labels later.
@@ -1085,6 +1115,35 @@ void DwarfDebug::beginModule() {
10851115
DICompileUnit *CUNode = *M->debug_compile_units_begin();
10861116
CompileUnit *CU = constructCompileUnit(CUNode);
10871117

1118+
// Global Variables.
1119+
for (auto *GVE : CUNode->getGlobalVariables()) {
1120+
// Don't bother adding DIGlobalVariableExpressions listed in the CU if we
1121+
// already know about the variable and it isn't adding a constant
1122+
// expression.
1123+
auto &GVMapEntry = GVMap[GVE->getVariable()];
1124+
auto *Expr = GVE->getExpression();
1125+
if (!GVMapEntry.size() || (Expr && Expr->isConstant()))
1126+
GVMapEntry.push_back({nullptr, Expr});
1127+
}
1128+
1129+
DenseSet<DIGlobalVariable *> Processed;
1130+
for (auto *GVE : CUNode->getGlobalVariables()) {
1131+
DIGlobalVariable *GV = GVE->getVariable();
1132+
1133+
// Skip variables in different addrspaces than global,
1134+
// SLM globals and constants will be handled later. This
1135+
// will avoid duplication.
1136+
if (GVMap.find(GV) == GVMap.end())
1137+
continue;
1138+
1139+
bool AllInGlobalAS = llvm::all_of(GVMap.find(GV)->second, [](const CompileUnit::GlobalExpr &GE) {
1140+
return GE.Var && GE.Var->getAddressSpace() == ADDRESS_SPACE_GLOBAL;
1141+
});
1142+
1143+
if (Processed.insert(GV).second && AllInGlobalAS)
1144+
CU->getOrCreateGlobalVariableDIE(GV, sortGlobalExprs(GVMap[GV]));
1145+
}
1146+
10881147
for (auto *DISP : DISubprogramNodes)
10891148
constructSubprogramDIE(CU, DISP);
10901149

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
;=========================== begin_copyright_notice ============================
2+
;
3+
; Copyright (C) 2025 Intel Corporation
4+
;
5+
; SPDX-License-Identifier: MIT
6+
;
7+
;============================ end_copyright_notice =============================
8+
9+
; Test generates DWARF for global variable.
10+
11+
; UNSUPPORTED: sys32
12+
13+
; REQUIRES: regkeys, oneapi-readelf, llvm-15-plus
14+
15+
; LLVM with opaque pointers:
16+
; RUN: llvm-as -opaque-pointers=1 %s -o %t
17+
; RUN: ocloc compile -llvm_input -file %t -device dg2 -options "-g -cl-opt-disable -igc_opts 'EnableOpaquePointersBackend=1, ElfDumpEnable=1, DumpUseShorterName=0, DebugDumpNamePrefix=%t_'"
18+
; RUN: oneapi-readelf --debug-dump %t_OCL_simd32_foo.elf | FileCheck %s
19+
20+
; LLVM with typed pointers/default pointer typing:
21+
; RUN: llvm-as -opaque-pointers=0 %s -o %t
22+
; RUN: ocloc compile -llvm_input -file %t -device dg2 -options "-g -cl-opt-disable -igc_opts 'ElfDumpEnable=1, DumpUseShorterName=0, DebugDumpNamePrefix=%t_'"
23+
; RUN: oneapi-readelf --debug-dump %t_OCL_simd32_foo.elf | FileCheck %s
24+
25+
@global = addrspace(1) global i64 0, align 8, !dbg !0
26+
; CHECK: DW_TAG_variable
27+
; CHECK-NEXT: DW_AT_name : global
28+
; CHECK-NEXT: DW_AT_type : {{.*}}
29+
; CHECK-NEXT: DW_AT_external : {{.*}}
30+
; CHECK-NEXT: DW_AT_decl_file : {{.*}}
31+
; CHECK-NEXT: DW_AT_decl_line : {{.*}}
32+
33+
; Function Attrs: nounwind
34+
define spir_kernel void @foo() #0 !dbg !6 {
35+
entry:
36+
ret void
37+
}
38+
39+
attributes #0 = { nounwind readnone }
40+
41+
!llvm.dbg.cu = !{!2}
42+
!llvm.module.flags = !{!9, !10}
43+
44+
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
45+
!1 = distinct !DIGlobalVariable(name: "global", scope: !2, file: !3, line: 5, type: !5, isLocal: false, isDefinition: true)
46+
!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 14.0.5", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4)
47+
!3 = !DIFile(filename: "global.cpp", directory: "/tmp")
48+
!4 = !{!0}
49+
!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
50+
!6 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 3, type: !7, flags: DIFlagPrototyped, unit: !2, retainedNodes: !11)
51+
!7 = !DISubroutineType(types: !8)
52+
!8 = !{null}
53+
!9 = !{i32 2, !"Dwarf Version", i32 4}
54+
!10 = !{i32 1, !"Debug Info Version", i32 3}
55+
!11 = !{}

0 commit comments

Comments
 (0)