Skip to content

Commit 94682e6

Browse files
committed
[flang] Add initial implementation of module variables.
This PR add supports for module variables and function. 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 45fed80 commit 94682e6

File tree

5 files changed

+190
-8
lines changed

5 files changed

+190
-8
lines changed

flang/lib/Optimizer/CodeGen/CodeGen.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2694,6 +2694,18 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
26942694
mlir::LogicalResult
26952695
matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
26962696
mlir::ConversionPatternRewriter &rewriter) const override {
2697+
2698+
mlir::LLVM::DIGlobalVariableExpressionAttr dbgExpr;
2699+
2700+
if (auto fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(global.getLoc())) {
2701+
if (auto gvAttr =
2702+
mlir::dyn_cast_or_null<mlir::LLVM::DIGlobalVariableAttr>(
2703+
fusedLoc.getMetadata())) {
2704+
dbgExpr = mlir::LLVM::DIGlobalVariableExpressionAttr::get(
2705+
global.getContext(), gvAttr, mlir::LLVM::DIExpressionAttr());
2706+
}
2707+
}
2708+
26972709
auto tyAttr = convertType(global.getType());
26982710
if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(global.getType()))
26992711
tyAttr = this->lowerTy().convertBoxTypeAsStruct(boxType);
@@ -2702,8 +2714,11 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
27022714
assert(attributeTypeIsCompatible(global.getContext(), initAttr, tyAttr));
27032715
auto linkage = convertLinkage(global.getLinkName());
27042716
auto isConst = global.getConstant().has_value();
2717+
mlir::SymbolRefAttr comdat;
2718+
llvm::ArrayRef<mlir::NamedAttribute> attrs;
27052719
auto g = rewriter.create<mlir::LLVM::GlobalOp>(
2706-
loc, tyAttr, isConst, linkage, global.getSymName(), initAttr);
2720+
loc, tyAttr, isConst, linkage, global.getSymName(), initAttr, 0, 0,
2721+
false, false, comdat, attrs, dbgExpr);
27072722

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

flang/lib/Optimizer/Transforms/AddDebugInfo.cpp

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "llvm/Support/FileSystem.h"
3535
#include "llvm/Support/Path.h"
3636
#include "llvm/Support/raw_ostream.h"
37+
#include <map>
3738

3839
namespace fir {
3940
#define GEN_PASS_DEF_ADDDEBUGINFO
@@ -48,10 +49,87 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
4849
public:
4950
AddDebugInfoPass(fir::AddDebugInfoOptions options) : Base(options) {}
5051
void runOnOperation() override;
52+
53+
private:
54+
std::map<std::string, mlir::LLVM::DIModuleAttr> moduleMap;
55+
56+
mlir::LLVM::DIModuleAttr getOrCreateModuleAttr(
57+
const std::string &name, mlir::LLVM::DIFileAttr fileAttr,
58+
mlir::LLVM::DIScopeAttr scope, unsigned line, bool decl);
59+
60+
void handleGlobalOp(fir::GlobalOp glocalOp, mlir::LLVM::DIFileAttr fileAttr,
61+
mlir::LLVM::DIScopeAttr scope);
5162
};
5263

64+
static uint32_t getLineFromLoc(mlir::Location loc) {
65+
uint32_t line = 1;
66+
if (auto fileLoc = mlir::dyn_cast<mlir::FileLineColLoc>(loc))
67+
line = fileLoc.getLine();
68+
return line;
69+
}
70+
5371
} // namespace
5472

73+
// The `module` does not have a first class representation in the `FIR`. We
74+
// extract information about it from the name of the identifiers and keep a
75+
// map to avoid duplication.
76+
mlir::LLVM::DIModuleAttr AddDebugInfoPass::getOrCreateModuleAttr(
77+
const std::string &name, mlir::LLVM::DIFileAttr fileAttr,
78+
mlir::LLVM::DIScopeAttr scope, unsigned line, bool decl) {
79+
mlir::MLIRContext *context = &getContext();
80+
mlir::LLVM::DIModuleAttr modAttr;
81+
if (auto iter{moduleMap.find(name)}; iter != moduleMap.end())
82+
modAttr = iter->second;
83+
else {
84+
modAttr = mlir::LLVM::DIModuleAttr::get(
85+
context, fileAttr, scope, mlir::StringAttr::get(context, name),
86+
mlir::StringAttr(), mlir::StringAttr(), mlir::StringAttr(), line, decl);
87+
moduleMap[name] = modAttr;
88+
}
89+
return modAttr;
90+
}
91+
92+
void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
93+
mlir::LLVM::DIFileAttr fileAttr,
94+
mlir::LLVM::DIScopeAttr scope) {
95+
mlir::ModuleOp module = getOperation();
96+
mlir::MLIRContext *context = &getContext();
97+
fir::DebugTypeGenerator typeGen(module);
98+
mlir::OpBuilder builder(context);
99+
100+
auto result = fir::NameUniquer::deconstruct(globalOp.getSymName());
101+
if (result.first != fir::NameUniquer::NameKind::VARIABLE)
102+
return;
103+
104+
unsigned line = getLineFromLoc(globalOp.getLoc());
105+
106+
// DWARF5 says following about the fortran modules:
107+
// A Fortran 90 module may also be represented by a module entry
108+
// (but no declaration attribute is warranted because Fortran has no concept
109+
// of a corresponding module body).
110+
// But in practice, compilers use declaration attribute with a module in cases
111+
// where module was defined in another source file (only being used in this
112+
// one). The hasInitializationBody() seems to provide the right information
113+
// but inverted. It is true where module is actually defined but false where
114+
// it is used.
115+
// FIXME: Currently we don't have the line number on which a module was
116+
// declared. We are using a best guess of line - 1 where line is the source
117+
// line of the first member of the module that we encounter.
118+
119+
if (!result.second.modules.empty())
120+
scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, scope,
121+
line - 1, !globalOp.hasInitializationBody());
122+
123+
auto diType = typeGen.convertType(globalOp.getType(), fileAttr, scope,
124+
globalOp.getLoc());
125+
auto gvAttr = mlir::LLVM::DIGlobalVariableAttr::get(
126+
context, scope, mlir::StringAttr::get(context, result.second.name),
127+
mlir::StringAttr::get(context, globalOp.getName()), fileAttr, line,
128+
diType, /*isLocalToUnit*/ false,
129+
/*isDefinition*/ globalOp.hasInitializationBody(), /* alignInBits*/ 0);
130+
globalOp->setLoc(builder.getFusedLoc({globalOp->getLoc()}, gvAttr));
131+
}
132+
55133
void AddDebugInfoPass::runOnOperation() {
56134
mlir::ModuleOp module = getOperation();
57135
mlir::MLIRContext *context = &getContext();
@@ -91,6 +169,10 @@ void AddDebugInfoPass::runOnOperation() {
91169
llvm::dwarf::getLanguage("DW_LANG_Fortran95"), fileAttr, producer,
92170
isOptimized, debugLevel);
93171

172+
module.walk([&](fir::GlobalOp globalOp) {
173+
handleGlobalOp(globalOp, fileAttr, cuAttr);
174+
});
175+
94176
module.walk([&](mlir::func::FuncOp funcOp) {
95177
mlir::Location l = funcOp->getLoc();
96178
// If fused location has already been created then nothing to do
@@ -131,8 +213,12 @@ void AddDebugInfoPass::runOnOperation() {
131213
mlir::LLVM::DIFileAttr funcFileAttr =
132214
mlir::LLVM::DIFileAttr::get(context, fileName, filePath);
133215

216+
unsigned line = 1;
217+
if (auto funcLoc = mlir::dyn_cast<mlir::FileLineColLoc>(l))
218+
line = funcLoc.getLine();
134219
// Only definitions need a distinct identifier and a compilation unit.
135220
mlir::DistinctAttr id;
221+
mlir::LLVM::DIScopeAttr Scope = fileAttr;
136222
mlir::LLVM::DICompileUnitAttr compilationUnit;
137223
mlir::LLVM::DISubprogramFlags subprogramFlags =
138224
mlir::LLVM::DISubprogramFlags{};
@@ -144,13 +230,13 @@ void AddDebugInfoPass::runOnOperation() {
144230
subprogramFlags =
145231
subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;
146232
}
147-
unsigned line = 1;
148-
if (auto funcLoc = mlir::dyn_cast<mlir::FileLineColLoc>(l))
149-
line = funcLoc.getLine();
233+
if (!result.second.modules.empty())
234+
Scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, cuAttr,
235+
line - 1, false);
150236

151237
auto spAttr = mlir::LLVM::DISubprogramAttr::get(
152-
context, id, compilationUnit, fileAttr, funcName, fullName,
153-
funcFileAttr, line, line, subprogramFlags, subTypeAttr);
238+
context, id, compilationUnit, Scope, funcName, fullName, funcFileAttr,
239+
line, line, subprogramFlags, subTypeAttr);
154240
funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr));
155241
});
156242
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
! RUN: %flang_fc1 -emit-fir -debug-info-kind=standalone -mmlir --mlir-print-debuginfo %s -o - | \
2+
! RUN: fir-opt --cg-rewrite --mlir-print-debuginfo | fir-opt --add-debug-info --mlir-print-debuginfo | FileCheck %s
3+
4+
! CHECK-DAG: #[[I4:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "integer", sizeInBits = 32, encoding = DW_ATE_signed>
5+
! CHECK-DAG: #[[R4:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "real", sizeInBits = 32, encoding = DW_ATE_float>
6+
! CHECK-DAG: #[[FILE:.*]] = #llvm.di_file<"debug-module-1.f90" {{.*}}>
7+
! CHECK-DAG: #[[CU:.*]] = #llvm.di_compile_unit<{{.*}}, file = #[[FILE]], {{.*}}>
8+
! CHECK-DAG: #[[MOD:.*]] = #llvm.di_module<file = #[[FILE]], scope = #[[CU]], name = "helper", {{.*}}>
9+
module helper
10+
! CHECK-DAG: #[[LOC1:.*]] = loc("{{.*}}debug-module-1.f90":[[@LINE+2]]{{.*}})
11+
! CHECK-DAG: #[[GLR:.*]] = #llvm.di_global_variable<scope = #[[MOD]], name = "glr", linkageName = "_QMhelperEglr", file = #[[FILE]], line = [[@LINE+1]], type = #[[R4]], isDefined = true>
12+
real glr
13+
! CHECK-DAG: #[[LOC2:.*]] = loc("{{.*}}debug-module-1.f90":[[@LINE+2]]{{.*}})
14+
! CHECK-DAG: #[[GLI:.*]] = #llvm.di_global_variable<scope = #[[MOD]], name = "gli", linkageName = "_QMhelperEgli", file = #[[FILE]], line = [[@LINE+1]], type = #[[I4]], isDefined = true>
15+
integer gli
16+
17+
contains
18+
! CHECK-DAG: #[[LOC3:.*]] = loc("{{.*}}debug-module-1.f90":[[@LINE+2]]{{.*}})
19+
! CHECK-DAG: #[[TEST:.*]] = #llvm.di_subprogram<{{.*}}compileUnit = #[[CU]], scope = #[[MOD]], name = "test", linkageName = "_QMhelperPtest", file = #[[FILE]], line = [[@LINE+1]], scopeLine = [[@LINE+1]]{{.*}}>
20+
subroutine test()
21+
glr = 12.34
22+
gli = 67
23+
24+
end subroutine
25+
end module helper
26+
27+
program test
28+
use helper
29+
implicit none
30+
31+
glr = 3.14
32+
gli = 2
33+
call test()
34+
35+
end program test
36+
37+
! CHECK-DAG: loc(fused<#[[GLR]]>[#[[LOC1]]])
38+
! CHECK-DAG: loc(fused<#[[GLI]]>[#[[LOC2]]])
39+
! CHECK-DAG: loc(fused<#[[TEST]]>[#[[LOC3]]])
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s
2+
3+
! CHECK-DAG: ![[FILE:.*]] = !DIFile(filename: {{.*}}debug-module-2.f90{{.*}})
4+
! CHECK-DAG: ![[FILE2:.*]] = !DIFile(filename: {{.*}}debug-module-2.f90{{.*}})
5+
! CHECK-DAG: ![[CU:.*]] = distinct !DICompileUnit({{.*}}file: ![[FILE]]{{.*}} globals: ![[GLOBALS:.*]])
6+
! CHECK-DAG: ![[MOD:.*]] = !DIModule(scope: ![[CU]], name: "helper", file: ![[FILE]]{{.*}})
7+
! CHECK-DAG: ![[R4:.*]] = !DIBasicType(name: "real", size: 32, encoding: DW_ATE_float)
8+
! CHECK-DAG: ![[I4:.*]] = !DIBasicType(name: "integer", size: 32, encoding: DW_ATE_signed)
9+
module helper
10+
! CHECK-DAG: ![[GLR:.*]] = distinct !DIGlobalVariable(name: "glr", linkageName: "_QMhelperEglr", scope: ![[MOD]], file: ![[FILE]], line: [[@LINE+2]], type: ![[R4]], isLocal: false, isDefinition: true)
11+
! CHECK-DAG: ![[GLRX:.*]] = !DIGlobalVariableExpression(var: ![[GLR]], expr: !DIExpression())
12+
real glr
13+
14+
! CHECK-DAG: ![[GLI:.*]] = distinct !DIGlobalVariable(name: "gli", linkageName: "_QMhelperEgli", scope: ![[MOD]], file: ![[FILE]], line: [[@LINE+2]], type: ![[I4]], isLocal: false, isDefinition: true)
15+
! CHECK-DAG: ![[GLIX:.*]] = !DIGlobalVariableExpression(var: ![[GLI]], expr: !DIExpression())
16+
integer gli
17+
18+
contains
19+
!CHECK-DAG: !DISubprogram(name: "test", linkageName: "_QMhelperPtest", scope: ![[MOD]], file: ![[FILE2]], line: [[@LINE+1]]{{.*}}unit: ![[CU]])
20+
subroutine test()
21+
glr = 12.34
22+
gli = 67
23+
24+
end subroutine
25+
end module helper
26+
27+
program test
28+
use helper
29+
implicit none
30+
31+
glr = 3.14
32+
gli = 2
33+
call test()
34+
35+
end program test
36+
37+
! CHECK-DAG: ![[GLOBALS]] = !{![[GLIX]], ![[GLRX]]}

mlir/lib/Target/LLVMIR/ModuleTranslation.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,10 +1030,15 @@ LogicalResult ModuleTranslation::convertGlobals() {
10301030
llvm::DIGlobalVariable *diGlobalVar = diGlobalExpr->getVariable();
10311031
var->addDebugInfo(diGlobalExpr);
10321032

1033+
// For fortran, the scope hierarchy can be
1034+
// variable -> module -> compile unit
1035+
llvm::DIScope *scope = diGlobalVar->getScope();
1036+
if (llvm::DIModule *mod = dyn_cast_if_present<llvm::DIModule>(scope))
1037+
scope = mod->getScope();
1038+
10331039
// Get the compile unit (scope) of the the global variable.
10341040
if (llvm::DICompileUnit *compileUnit =
1035-
dyn_cast_if_present<llvm::DICompileUnit>(
1036-
diGlobalVar->getScope())) {
1041+
dyn_cast_if_present<llvm::DICompileUnit>(scope)) {
10371042
// Update the compile unit with this incoming global variable expression
10381043
// during the finalizing step later.
10391044
allGVars[compileUnit].push_back(diGlobalExpr);

0 commit comments

Comments
 (0)