Skip to content

Commit 20c6b9f

Browse files
authored
[flang][debug] Fix issues with local variables. (#98661)
This PR fixes 2 similar issues. 1. As reported in #97476, flang generated executable has inconsistent behavior regarding values of the local array variables. 2. Variable with save attribute would not show up in debugger. The reason behind is same for both cases. If a local variable has storage which extends beyond function lifetime, the way to represent it in the debug info is through a global variable whose scope is limited to the function. This is what is used for static local variable in C. Previously local array worked in cases they were on stack. But will not show up if they had a global storage. To fix this, if we can get a corresponding `GlobalOp` for a variable while processing `DeclareOp`, we treat it the variable as global with scope set appropriately. A new FIR test is added. A previous Integration test has been adjusted as to not expect local variables for local arrays. With this fix in place, all the issues described in #97476 go away. It also fixes a lot of fails in GDB's fortran testsuite. Fixes #97476.
1 parent e94e72a commit 20c6b9f

File tree

3 files changed

+94
-19
lines changed

3 files changed

+94
-19
lines changed

flang/lib/Optimizer/Transforms/AddDebugInfo.cpp

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
4949
void handleDeclareOp(fir::cg::XDeclareOp declOp,
5050
mlir::LLVM::DIFileAttr fileAttr,
5151
mlir::LLVM::DIScopeAttr scopeAttr,
52-
fir::DebugTypeGenerator &typeGen);
52+
fir::DebugTypeGenerator &typeGen,
53+
mlir::SymbolTable *symbolTable);
5354

5455
public:
5556
AddDebugInfoPass(fir::AddDebugInfoOptions options) : Base(options) {}
@@ -63,7 +64,8 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
6364
mlir::LLVM::DIScopeAttr scope, unsigned line, bool decl);
6465

6566
void handleGlobalOp(fir::GlobalOp glocalOp, mlir::LLVM::DIFileAttr fileAttr,
66-
mlir::LLVM::DIScopeAttr scope);
67+
mlir::LLVM::DIScopeAttr scope,
68+
mlir::SymbolTable *symbolTable);
6769
};
6870

6971
static uint32_t getLineFromLoc(mlir::Location loc) {
@@ -73,19 +75,32 @@ static uint32_t getLineFromLoc(mlir::Location loc) {
7375
return line;
7476
}
7577

78+
bool debugInfoIsAlreadySet(mlir::Location loc) {
79+
if (mlir::isa<mlir::FusedLoc>(loc))
80+
return true;
81+
return false;
82+
}
83+
7684
} // namespace
7785

7886
void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,
7987
mlir::LLVM::DIFileAttr fileAttr,
8088
mlir::LLVM::DIScopeAttr scopeAttr,
81-
fir::DebugTypeGenerator &typeGen) {
89+
fir::DebugTypeGenerator &typeGen,
90+
mlir::SymbolTable *symbolTable) {
8291
mlir::MLIRContext *context = &getContext();
8392
mlir::OpBuilder builder(context);
8493
auto result = fir::NameUniquer::deconstruct(declOp.getUniqName());
8594

8695
if (result.first != fir::NameUniquer::NameKind::VARIABLE)
8796
return;
8897

98+
// If this DeclareOp actually represents a global then treat it as such.
99+
if (auto global = symbolTable->lookup<fir::GlobalOp>(declOp.getUniqName())) {
100+
handleGlobalOp(global, fileAttr, scopeAttr, symbolTable);
101+
return;
102+
}
103+
89104
// Only accept local variables.
90105
if (result.second.procs.empty())
91106
return;
@@ -138,7 +153,10 @@ mlir::LLVM::DIModuleAttr AddDebugInfoPass::getOrCreateModuleAttr(
138153

139154
void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
140155
mlir::LLVM::DIFileAttr fileAttr,
141-
mlir::LLVM::DIScopeAttr scope) {
156+
mlir::LLVM::DIScopeAttr scope,
157+
mlir::SymbolTable *symbolTable) {
158+
if (debugInfoIsAlreadySet(globalOp.getLoc()))
159+
return;
142160
mlir::ModuleOp module = getOperation();
143161
mlir::MLIRContext *context = &getContext();
144162
fir::DebugTypeGenerator typeGen(module);
@@ -163,12 +181,19 @@ void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
163181
// declared. We are using a best guess of line - 1 where line is the source
164182
// line of the first member of the module that we encounter.
165183

166-
if (result.second.modules.empty())
167-
return;
184+
if (result.second.procs.empty()) {
185+
// Only look for module if this variable is not part of a function.
186+
if (result.second.modules.empty())
187+
return;
168188

169-
scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, scope,
170-
line - 1, !globalOp.isInitialized());
189+
// Modules are generated at compile unit scope
190+
if (mlir::LLVM::DISubprogramAttr sp =
191+
mlir::dyn_cast_if_present<mlir::LLVM::DISubprogramAttr>(scope))
192+
scope = sp.getCompileUnit();
171193

194+
scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, scope,
195+
line - 1, !globalOp.isInitialized());
196+
}
172197
mlir::LLVM::DITypeAttr diType = typeGen.convertType(
173198
globalOp.getType(), fileAttr, scope, globalOp.getLoc());
174199
auto gvAttr = mlir::LLVM::DIGlobalVariableAttr::get(
@@ -182,6 +207,7 @@ void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
182207
void AddDebugInfoPass::runOnOperation() {
183208
mlir::ModuleOp module = getOperation();
184209
mlir::MLIRContext *context = &getContext();
210+
mlir::SymbolTable symbolTable(module);
185211
mlir::OpBuilder builder(context);
186212
llvm::StringRef fileName;
187213
std::string filePath;
@@ -218,17 +244,11 @@ void AddDebugInfoPass::runOnOperation() {
218244
llvm::dwarf::getLanguage("DW_LANG_Fortran95"), fileAttr, producer,
219245
isOptimized, debugLevel);
220246

221-
if (debugLevel == mlir::LLVM::DIEmissionKind::Full) {
222-
// Process 'GlobalOp' only if full debug info is requested.
223-
for (auto globalOp : module.getOps<fir::GlobalOp>())
224-
handleGlobalOp(globalOp, fileAttr, cuAttr);
225-
}
226-
227247
module.walk([&](mlir::func::FuncOp funcOp) {
228248
mlir::Location l = funcOp->getLoc();
229249
// If fused location has already been created then nothing to do
230250
// Otherwise, create a fused location.
231-
if (mlir::dyn_cast<mlir::FusedLoc>(l))
251+
if (debugInfoIsAlreadySet(l))
232252
return;
233253

234254
unsigned int CC = (funcOp.getName() == fir::NameUniquer::doProgramEntry())
@@ -293,9 +313,15 @@ void AddDebugInfoPass::runOnOperation() {
293313
return;
294314

295315
funcOp.walk([&](fir::cg::XDeclareOp declOp) {
296-
handleDeclareOp(declOp, fileAttr, spAttr, typeGen);
316+
handleDeclareOp(declOp, fileAttr, spAttr, typeGen, &symbolTable);
297317
});
298318
});
319+
// Process any global which was not processed through DeclareOp.
320+
if (debugLevel == mlir::LLVM::DIEmissionKind::Full) {
321+
// Process 'GlobalOp' only if full debug info is requested.
322+
for (auto globalOp : module.getOps<fir::GlobalOp>())
323+
handleGlobalOp(globalOp, fileAttr, cuAttr, &symbolTable);
324+
}
299325
}
300326

301327
std::unique_ptr<mlir::Pass>

flang/test/Integration/debug-fixed-array-type-2.f90

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,17 @@ function fn1(a1, b1, c1) result (res)
2323
! CHECK-DAG: ![[R1:.*]] = !DISubrange(count: 3, lowerBound: 1)
2424
! CHECK-DAG: ![[SUB1:.*]] = !{![[R1]]}
2525
! CHECK-DAG: ![[D1TY:.*]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[INT]], elements: ![[SUB1]])
26-
! CHECK-DAG: !DILocalVariable(name: "d1"{{.*}}type: ![[D1TY]])
2726

2827
! CHECK-DAG: ![[R21:.*]] = !DISubrange(count: 2, lowerBound: 1)
2928
! CHECK-DAG: ![[R22:.*]] = !DISubrange(count: 5, lowerBound: 1)
3029
! CHECK-DAG: ![[SUB2:.*]] = !{![[R21]], ![[R22]]}
3130
! CHECK-DAG: ![[D2TY:.*]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[INT]], elements: ![[SUB2]])
32-
! CHECK-DAG: !DILocalVariable(name: "d2"{{.*}}type: ![[D2TY]])
3331

3432
! CHECK-DAG: ![[R31:.*]] = !DISubrange(count: 6, lowerBound: 1)
3533
! CHECK-DAG: ![[R32:.*]] = !DISubrange(count: 8, lowerBound: 1)
3634
! CHECK-DAG: ![[R33:.*]] = !DISubrange(count: 7, lowerBound: 1)
3735
! CHECK-DAG: ![[SUB3:.*]] = !{![[R31]], ![[R32]], ![[R33]]}
3836
! CHECK-DAG: ![[D3TY:.*]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[REAL]], elements: ![[SUB3]])
39-
! CHECK-DAG: !DILocalVariable(name: "d3"{{.*}}type: ![[D3TY]])
4037

4138
! CHECK-DAG: !DILocalVariable(name: "a1", arg: 1{{.*}}type: ![[D1TY]])
4239
! CHECK-DAG: !DILocalVariable(name: "b1", arg: 2{{.*}}type: ![[D2TY]])
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s
2+
3+
module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<i64, dense<64> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr<272>, dense<64> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<271>, dense<32> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<270>, dense<32> : vector<4xi64>>, #dlti.dl_entry<f128, dense<128> : vector<2xi64>>, #dlti.dl_entry<f80, dense<128> : vector<2xi64>>, #dlti.dl_entry<i128, dense<128> : vector<2xi64>>, #dlti.dl_entry<i8, dense<8> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr, dense<64> : vector<4xi64>>, #dlti.dl_entry<i1, dense<8> : vector<2xi64>>, #dlti.dl_entry<f16, dense<16> : vector<2xi64>>, #dlti.dl_entry<f64, dense<64> : vector<2xi64>>, #dlti.dl_entry<i32, dense<32> : vector<2xi64>>, #dlti.dl_entry<i16, dense<16> : vector<2xi64>>, #dlti.dl_entry<"dlti.stack_alignment", 128 : i64>, #dlti.dl_entry<"dlti.endianness", "little">>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"} {
4+
func.func @_QMexamplePmod_sub() {
5+
%c2 = arith.constant 2 : index
6+
%1 = fir.address_of(@_QMexampleEmod_arr) : !fir.ref<!fir.array<2x2xi32>>
7+
%2 = fircg.ext_declare %1(%c2, %c2) {uniq_name = "_QMexampleEmod_arr"} : (!fir.ref<!fir.array<2x2xi32>>, index, index) -> !fir.ref<!fir.array<2x2xi32>> loc(#loc4)
8+
%3 = fir.address_of(@_QMexampleFmod_subEss) : !fir.ref<i32>
9+
%4 = fircg.ext_declare %3 {uniq_name = "_QMexampleFmod_subEss"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc5)
10+
return
11+
} loc(#loc6)
12+
func.func @_QQmain() attributes {fir.bindc_name = "test"} {
13+
%c3 = arith.constant 3 : index
14+
%c4 = arith.constant 4 : index
15+
%1 = fir.address_of(@_QFEarr) : !fir.ref<!fir.array<3x4xi32>>
16+
%2 = fircg.ext_declare %1(%c3, %c4) {uniq_name = "_QFEarr"} : (!fir.ref<!fir.array<3x4xi32>>, index, index) -> !fir.ref<!fir.array<3x4xi32>> loc(#loc2)
17+
%3 = fir.address_of(@_QFEs) : !fir.ref<i32>
18+
%4 = fircg.ext_declare %3 {uniq_name = "_QFEs"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc3)
19+
return
20+
} loc(#loc1)
21+
fir.global @_QMexampleEmod_arr : !fir.array<2x2xi32> {
22+
%0 = fir.zero_bits !fir.array<2x2xi32>
23+
fir.has_value %0 : !fir.array<2x2xi32>
24+
} loc(#loc4)
25+
fir.global internal @_QMexampleFmod_subEss : i32 {
26+
%c2_i32 = arith.constant 2 : i32
27+
fir.has_value %c2_i32 : i32
28+
} loc(#loc5)
29+
fir.global internal @_QFEarr : !fir.array<3x4xi32> {
30+
%0 = fir.zero_bits !fir.array<3x4xi32>
31+
fir.has_value %0 : !fir.array<3x4xi32>
32+
} loc(#loc2)
33+
fir.global internal @_QFEs : i32 {
34+
%c2_i32 = arith.constant 2 : i32
35+
fir.has_value %c2_i32 : i32
36+
} loc(#loc3)
37+
}
38+
#loc1 = loc("test.f90":21:1)
39+
#loc2 = loc("test.f90":22:1)
40+
#loc3 = loc("test.f90":23:1)
41+
#loc4 = loc("test.f90":5:1)
42+
#loc5 = loc("test.f90":12:1)
43+
#loc6 = loc("test.f90":10:1)
44+
45+
// CHECK-DAG: #[[CU:.*]] = #llvm.di_compile_unit<{{.*}}>
46+
// CHECK-DAG: #[[MOD:.*]] = #llvm.di_module<{{.*}}scope = #[[CU]]{{.*}}name = "example"{{.*}}>
47+
// CHECK-DAG: #[[SP:.*]] = #llvm.di_subprogram<{{.*}}name = "_QQmain"{{.*}}>
48+
// CHECK-DAG: #[[MOD_SP:.*]] = #llvm.di_subprogram<{{.*}}name = "mod_sub"{{.*}}>
49+
// CHECK-DAG: #llvm.di_global_variable<scope = #[[SP]], name = "arr"{{.*}}line = 22{{.*}}>
50+
// CHECK-DAG: #llvm.di_global_variable<scope = #[[SP]], name = "s"{{.*}}line = 23{{.*}}>
51+
// CHECK-DAG: #llvm.di_global_variable<scope = #[[MOD_SP]], name = "ss"{{.*}}line = 12{{.*}}>
52+
// CHECK-DAG: #llvm.di_global_variable<scope = #[[MOD]], name = "mod_arr"{{.*}}line = 5{{.*}}>

0 commit comments

Comments
 (0)