Skip to content

Commit f156b9c

Browse files
authored
[flang] Add debug information for module variables. (llvm#91582)
This PR add debug info for module variables. The module variables are added as global variables but their scope is set to module instead of compile unit. The scope of function declared inside a module is also set accordingly. After this patch, a module variable could be evaluated in the GDB as `p helper::gli` where helper is name of the module and gli is the name of the variable. A future patch will add the import module functionality which will remove the need to prefix the name with helper::. The line number where is module is declared is a best guess at the moment as this information is not part of the GlobalOp.
1 parent 1f5cd3a commit f156b9c

File tree

5 files changed

+217
-3
lines changed

5 files changed

+217
-3
lines changed

flang/lib/Optimizer/CodeGen/CodeGen.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2716,6 +2716,18 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
27162716
mlir::LogicalResult
27172717
matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
27182718
mlir::ConversionPatternRewriter &rewriter) const override {
2719+
2720+
mlir::LLVM::DIGlobalVariableExpressionAttr dbgExpr;
2721+
2722+
if (auto fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(global.getLoc())) {
2723+
if (auto gvAttr =
2724+
mlir::dyn_cast_or_null<mlir::LLVM::DIGlobalVariableAttr>(
2725+
fusedLoc.getMetadata())) {
2726+
dbgExpr = mlir::LLVM::DIGlobalVariableExpressionAttr::get(
2727+
global.getContext(), gvAttr, mlir::LLVM::DIExpressionAttr());
2728+
}
2729+
}
2730+
27192731
auto tyAttr = convertType(global.getType());
27202732
if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(global.getType()))
27212733
tyAttr = this->lowerTy().convertBoxTypeAsStruct(boxType);
@@ -2724,8 +2736,11 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
27242736
assert(attributeTypeIsCompatible(global.getContext(), initAttr, tyAttr));
27252737
auto linkage = convertLinkage(global.getLinkName());
27262738
auto isConst = global.getConstant().has_value();
2739+
mlir::SymbolRefAttr comdat;
2740+
llvm::ArrayRef<mlir::NamedAttribute> attrs;
27272741
auto g = rewriter.create<mlir::LLVM::GlobalOp>(
2728-
loc, tyAttr, isConst, linkage, global.getSymName(), initAttr);
2742+
loc, tyAttr, isConst, linkage, global.getSymName(), initAttr, 0, 0,
2743+
false, false, comdat, attrs, dbgExpr);
27292744

27302745
auto module = global->getParentOfType<mlir::ModuleOp>();
27312746
// Add comdat if necessary

flang/lib/Optimizer/Transforms/AddDebugInfo.cpp

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
5454
public:
5555
AddDebugInfoPass(fir::AddDebugInfoOptions options) : Base(options) {}
5656
void runOnOperation() override;
57+
58+
private:
59+
llvm::StringMap<mlir::LLVM::DIModuleAttr> moduleMap;
60+
61+
mlir::LLVM::DIModuleAttr getOrCreateModuleAttr(
62+
const std::string &name, mlir::LLVM::DIFileAttr fileAttr,
63+
mlir::LLVM::DIScopeAttr scope, unsigned line, bool decl);
64+
65+
void handleGlobalOp(fir::GlobalOp glocalOp, mlir::LLVM::DIFileAttr fileAttr,
66+
mlir::LLVM::DIScopeAttr scope);
5767
};
5868

5969
static uint32_t getLineFromLoc(mlir::Location loc) {
@@ -99,6 +109,70 @@ void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,
99109
declOp->setLoc(builder.getFusedLoc({declOp->getLoc()}, localVarAttr));
100110
}
101111

112+
// The `module` does not have a first class representation in the `FIR`. We
113+
// extract information about it from the name of the identifiers and keep a
114+
// map to avoid duplication.
115+
mlir::LLVM::DIModuleAttr AddDebugInfoPass::getOrCreateModuleAttr(
116+
const std::string &name, mlir::LLVM::DIFileAttr fileAttr,
117+
mlir::LLVM::DIScopeAttr scope, unsigned line, bool decl) {
118+
mlir::MLIRContext *context = &getContext();
119+
mlir::LLVM::DIModuleAttr modAttr;
120+
if (auto iter{moduleMap.find(name)}; iter != moduleMap.end()) {
121+
modAttr = iter->getValue();
122+
} else {
123+
modAttr = mlir::LLVM::DIModuleAttr::get(
124+
context, fileAttr, scope, mlir::StringAttr::get(context, name),
125+
/* configMacros */ mlir::StringAttr(),
126+
/* includePath */ mlir::StringAttr(),
127+
/* apinotes */ mlir::StringAttr(), line, decl);
128+
moduleMap[name] = modAttr;
129+
}
130+
return modAttr;
131+
}
132+
133+
void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
134+
mlir::LLVM::DIFileAttr fileAttr,
135+
mlir::LLVM::DIScopeAttr scope) {
136+
mlir::ModuleOp module = getOperation();
137+
mlir::MLIRContext *context = &getContext();
138+
fir::DebugTypeGenerator typeGen(module);
139+
mlir::OpBuilder builder(context);
140+
141+
std::pair result = fir::NameUniquer::deconstruct(globalOp.getSymName());
142+
if (result.first != fir::NameUniquer::NameKind::VARIABLE)
143+
return;
144+
145+
unsigned line = getLineFromLoc(globalOp.getLoc());
146+
147+
// DWARF5 says following about the fortran modules:
148+
// A Fortran 90 module may also be represented by a module entry
149+
// (but no declaration attribute is warranted because Fortran has no concept
150+
// of a corresponding module body).
151+
// But in practice, compilers use declaration attribute with a module in cases
152+
// where module was defined in another source file (only being used in this
153+
// one). The isInitialized() seems to provide the right information
154+
// but inverted. It is true where module is actually defined but false where
155+
// it is used.
156+
// FIXME: Currently we don't have the line number on which a module was
157+
// declared. We are using a best guess of line - 1 where line is the source
158+
// line of the first member of the module that we encounter.
159+
160+
if (result.second.modules.empty())
161+
return;
162+
163+
scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, scope,
164+
line - 1, !globalOp.isInitialized());
165+
166+
mlir::LLVM::DITypeAttr diType = typeGen.convertType(
167+
globalOp.getType(), fileAttr, scope, globalOp.getLoc());
168+
auto gvAttr = mlir::LLVM::DIGlobalVariableAttr::get(
169+
context, scope, mlir::StringAttr::get(context, result.second.name),
170+
mlir::StringAttr::get(context, globalOp.getName()), fileAttr, line,
171+
diType, /*isLocalToUnit*/ false,
172+
/*isDefinition*/ globalOp.isInitialized(), /* alignInBits*/ 0);
173+
globalOp->setLoc(builder.getFusedLoc({globalOp->getLoc()}, gvAttr));
174+
}
175+
102176
void AddDebugInfoPass::runOnOperation() {
103177
mlir::ModuleOp module = getOperation();
104178
mlir::MLIRContext *context = &getContext();
@@ -138,6 +212,12 @@ void AddDebugInfoPass::runOnOperation() {
138212
llvm::dwarf::getLanguage("DW_LANG_Fortran95"), fileAttr, producer,
139213
isOptimized, debugLevel);
140214

215+
if (debugLevel == mlir::LLVM::DIEmissionKind::Full) {
216+
// Process 'GlobalOp' only if full debug info is requested.
217+
for (auto globalOp : module.getOps<fir::GlobalOp>())
218+
handleGlobalOp(globalOp, fileAttr, cuAttr);
219+
}
220+
141221
module.walk([&](mlir::func::FuncOp funcOp) {
142222
mlir::Location l = funcOp->getLoc();
143223
// If fused location has already been created then nothing to do
@@ -180,6 +260,7 @@ void AddDebugInfoPass::runOnOperation() {
180260

181261
// Only definitions need a distinct identifier and a compilation unit.
182262
mlir::DistinctAttr id;
263+
mlir::LLVM::DIScopeAttr Scope = fileAttr;
183264
mlir::LLVM::DICompileUnitAttr compilationUnit;
184265
mlir::LLVM::DISubprogramFlags subprogramFlags =
185266
mlir::LLVM::DISubprogramFlags{};
@@ -192,9 +273,13 @@ void AddDebugInfoPass::runOnOperation() {
192273
subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;
193274
}
194275
unsigned line = getLineFromLoc(l);
276+
if (!result.second.modules.empty())
277+
Scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, cuAttr,
278+
line - 1, false);
279+
195280
auto spAttr = mlir::LLVM::DISubprogramAttr::get(
196-
context, id, compilationUnit, fileAttr, funcName, fullName,
197-
funcFileAttr, line, line, subprogramFlags, subTypeAttr);
281+
context, id, compilationUnit, Scope, funcName, fullName, funcFileAttr,
282+
line, line, subprogramFlags, subTypeAttr);
198283
funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr));
199284

200285
// Don't process variables if user asked for line tables only.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s
2+
! RUN: %flang_fc1 -emit-llvm -debug-info-kind=line-tables-only %s -o - | FileCheck --check-prefix=LINEONLY %s
3+
4+
! CHECK-DAG: ![[FILE:.*]] = !DIFile(filename: {{.*}}debug-module-2.f90{{.*}})
5+
! CHECK-DAG: ![[FILE2:.*]] = !DIFile(filename: {{.*}}debug-module-2.f90{{.*}})
6+
! CHECK-DAG: ![[CU:.*]] = distinct !DICompileUnit({{.*}}file: ![[FILE]]{{.*}} globals: ![[GLOBALS:.*]])
7+
! CHECK-DAG: ![[MOD:.*]] = !DIModule(scope: ![[CU]], name: "helper", file: ![[FILE]]{{.*}})
8+
! CHECK-DAG: ![[R4:.*]] = !DIBasicType(name: "real", size: 32, encoding: DW_ATE_float)
9+
! CHECK-DAG: ![[I4:.*]] = !DIBasicType(name: "integer", size: 32, encoding: DW_ATE_signed)
10+
module helper
11+
! CHECK-DAG: ![[GLR:.*]] = distinct !DIGlobalVariable(name: "glr", linkageName: "_QMhelperEglr", scope: ![[MOD]], file: ![[FILE]], line: [[@LINE+2]], type: ![[R4]], isLocal: false, isDefinition: true)
12+
! CHECK-DAG: ![[GLRX:.*]] = !DIGlobalVariableExpression(var: ![[GLR]], expr: !DIExpression())
13+
real glr
14+
15+
! CHECK-DAG: ![[GLI:.*]] = distinct !DIGlobalVariable(name: "gli", linkageName: "_QMhelperEgli", scope: ![[MOD]], file: ![[FILE]], line: [[@LINE+2]], type: ![[I4]], isLocal: false, isDefinition: true)
16+
! CHECK-DAG: ![[GLIX:.*]] = !DIGlobalVariableExpression(var: ![[GLI]], expr: !DIExpression())
17+
integer gli
18+
19+
contains
20+
!CHECK-DAG: !DISubprogram(name: "test", linkageName: "_QMhelperPtest", scope: ![[MOD]], file: ![[FILE2]], line: [[@LINE+1]]{{.*}}unit: ![[CU]])
21+
subroutine test()
22+
glr = 12.34
23+
gli = 67
24+
25+
end subroutine
26+
end module helper
27+
28+
program test
29+
use helper
30+
implicit none
31+
32+
glr = 3.14
33+
gli = 2
34+
call test()
35+
36+
end program test
37+
38+
! CHECK-DAG: ![[GLOBALS]] = !{![[GLIX]], ![[GLRX]]}
39+
! LINEONLY-NOT: DIGlobalVariable
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s
2+
3+
4+
module attributes {} {
5+
fir.global @_QMhelperEgli : i32 {
6+
%0 = fir.zero_bits i32
7+
fir.has_value %0 : i32
8+
} loc(#loc1)
9+
fir.global @_QMhelperEglr : f32 {
10+
%0 = fir.zero_bits f32
11+
fir.has_value %0 : f32
12+
} loc(#loc2)
13+
func.func @_QMhelperPtest() {
14+
%c67_i32 = arith.constant 67 : i32
15+
%cst = arith.constant 1.234000e+01 : f32
16+
%0 = fir.address_of(@_QMhelperEgli) : !fir.ref<i32>
17+
%1 = fir.address_of(@_QMhelperEglr) : !fir.ref<f32>
18+
fir.store %cst to %1 : !fir.ref<f32>
19+
fir.store %c67_i32 to %0 : !fir.ref<i32>
20+
return
21+
} loc(#loc3)
22+
}
23+
#loc1 = loc("test.f90":12:11)
24+
#loc2 = loc("test.f90":15:8)
25+
#loc3 = loc("test.f90":20:5)
26+
27+
// CHECK-DAG: #[[I4:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "integer", sizeInBits = 32, encoding = DW_ATE_signed>
28+
// CHECK-DAG: #[[R4:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "real", sizeInBits = 32, encoding = DW_ATE_float>
29+
// CHECK-DAG: #[[CU:.*]] = #llvm.di_compile_unit<{{.*}}>
30+
// CHECK-DAG: #[[MOD:.*]] = #llvm.di_module<{{.*}}scope = #[[CU]], name = "helper"{{.*}}>
31+
// CHECK-DAG: #[[LOC1:.*]] = loc("{{.*}}test.f90":12{{.*}})
32+
// CHECK-DAG: #[[GLI:.*]] = #llvm.di_global_variable<scope = #[[MOD]], name = "gli", linkageName = "_QMhelperEgli"{{.*}}line = 12, type = #[[I4]], isDefined = true>
33+
// CHECK-DAG: #[[LOC2:.*]] = loc("{{.*}}test.f90":15{{.*}})
34+
// CHECK-DAG: #[[GLR:.*]] = #llvm.di_global_variable<scope = #[[MOD]], name = "glr", linkageName = "_QMhelperEglr"{{.*}}line = 15, type = #[[R4]], isDefined = true>
35+
// CHECK-DAG: #[[LOC3:.*]] = loc("{{.*}}test.f90":20{{.*}})
36+
// CHECK-DAG: #[[TEST:.*]] = #llvm.di_subprogram<{{.*}}compileUnit = #[[CU]], scope = #[[MOD]], name = "test", linkageName = "_QMhelperPtest"{{.*}}line = 20, scopeLine = 20{{.*}}>
37+
// CHECK-DAG: loc(fused<#[[GLI]]>[#[[LOC1]]])
38+
// CHECK-DAG: loc(fused<#[[GLR]]>[#[[LOC2]]])
39+
// CHECK-DAG: loc(fused<#[[TEST]]>[#[[LOC3]]])
40+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: fir-opt --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" --mlir-print-debuginfo %s | FileCheck %s
2+
3+
module {
4+
fir.global @_QMhelperEgli : i32 {
5+
%0 = fir.zero_bits i32
6+
fir.has_value %0 : i32
7+
} loc(#loc3)
8+
fir.global @_QMhelperEglr : f32 {
9+
%0 = fir.zero_bits f32
10+
fir.has_value %0 : f32
11+
} loc(#loc4)
12+
}
13+
#di_basic_type = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "integer", sizeInBits = 32, encoding = DW_ATE_signed>
14+
#di_basic_type1 = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "real", sizeInBits = 32, encoding = DW_ATE_float>
15+
16+
#di_file = #llvm.di_file<"test.f90" in "">
17+
#di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
18+
19+
#di_compile_unit = #llvm.di_compile_unit<id = distinct[0]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, producer = "flang version 19.0.0 (/home/haqadeer/work/llvm-project/flang 5d5c73cad421bdca6e43e1cc10704ff160f1a33e)", isOptimized = false, emissionKind = Full>
20+
#di_module = #llvm.di_module<file = #di_file, scope = #di_compile_unit, name = "helper", line = 11>
21+
#di_global_variable = #llvm.di_global_variable<scope = #di_module, name = "gli", linkageName = "_QMhelperEgli", file = #di_file, line = 12, type = #di_basic_type, isDefined = true>
22+
#di_global_variable1 = #llvm.di_global_variable<scope = #di_module, name = "glr", linkageName = "_QMhelperEglr", file = #di_file, line = 15, type = #di_basic_type1, isDefined = true>
23+
24+
#loc1 = loc("test.f90":12:11)
25+
#loc2 = loc("test.f90":15:8)
26+
#loc3 = loc(fused<#di_global_variable>[#loc1])
27+
#loc4 = loc(fused<#di_global_variable1>[#loc2])
28+
29+
30+
// CHECK-DAG: #[[GLI:.*]] = #llvm.di_global_variable<{{.*}}name = "gli", linkageName = "_QMhelperEgli"{{.*}}>
31+
// CHECK-DAG: #[[GLR:.*]] = #llvm.di_global_variable<{{.*}}name = "glr", linkageName = "_QMhelperEglr"{{.*}}>
32+
// CHECK-DAG: #[[GLIE:.*]] = #llvm.di_global_variable_expression<var = #[[GLI]]>
33+
// CHECK-DAG: #[[GLRE:.*]] = #llvm.di_global_variable_expression<var = #[[GLR]]>
34+
// CHECK-DAG: llvm.mlir.global{{.*}}@_QMhelperEgli() {{{.*}}dbg_expr = #[[GLIE]]}
35+
// CHECK-DAG: llvm.mlir.global{{.*}}@_QMhelperEglr() {{{.*}}dbg_expr = #[[GLRE]]}

0 commit comments

Comments
 (0)