1 //===-------------- AddDebugInfo.cpp -- add debug info -------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 //===----------------------------------------------------------------------===//
11 /// This pass populates some debug information for the module and functions.
12 //===----------------------------------------------------------------------===//
14 #include "DebugTypeGenerator.h"
15 #include "flang/Common/Version.h"
16 #include "flang/Optimizer/Builder/FIRBuilder.h"
17 #include "flang/Optimizer/Builder/Todo.h"
18 #include "flang/Optimizer/CodeGen/CGOps.h"
19 #include "flang/Optimizer/Dialect/FIRDialect.h"
20 #include "flang/Optimizer/Dialect/FIROps.h"
21 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
22 #include "flang/Optimizer/Dialect/FIRType.h"
23 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
24 #include "flang/Optimizer/Support/InternalNames.h"
25 #include "flang/Optimizer/Transforms/Passes.h"
26 #include "mlir/Dialect/Func/IR/FuncOps.h"
27 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
28 #include "mlir/IR/Matchers.h"
29 #include "mlir/IR/TypeUtilities.h"
30 #include "mlir/Pass/Pass.h"
31 #include "mlir/Transforms/DialectConversion.h"
32 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
33 #include "mlir/Transforms/RegionUtils.h"
34 #include "llvm/BinaryFormat/Dwarf.h"
35 #include "llvm/Support/Debug.h"
36 #include "llvm/Support/FileSystem.h"
37 #include "llvm/Support/Path.h"
38 #include "llvm/Support/raw_ostream.h"
41 #define GEN_PASS_DEF_ADDDEBUGINFO
42 #include "flang/Optimizer/Transforms/Passes.h.inc"
45 #define DEBUG_TYPE "flang-add-debug-info"
49 class AddDebugInfoPass
: public fir::impl::AddDebugInfoBase
<AddDebugInfoPass
> {
50 void handleDeclareOp(fir::cg::XDeclareOp declOp
,
51 mlir::LLVM::DIFileAttr fileAttr
,
52 mlir::LLVM::DIScopeAttr scopeAttr
,
53 fir::DebugTypeGenerator
&typeGen
,
54 mlir::SymbolTable
*symbolTable
);
57 AddDebugInfoPass(fir::AddDebugInfoOptions options
) : Base(options
) {}
58 void runOnOperation() override
;
61 llvm::StringMap
<mlir::LLVM::DIModuleAttr
> moduleMap
;
63 mlir::LLVM::DIModuleAttr
getOrCreateModuleAttr(
64 const std::string
&name
, mlir::LLVM::DIFileAttr fileAttr
,
65 mlir::LLVM::DIScopeAttr scope
, unsigned line
, bool decl
);
67 void handleGlobalOp(fir::GlobalOp glocalOp
, mlir::LLVM::DIFileAttr fileAttr
,
68 mlir::LLVM::DIScopeAttr scope
,
69 fir::DebugTypeGenerator
&typeGen
,
70 mlir::SymbolTable
*symbolTable
,
71 fir::cg::XDeclareOp declOp
);
72 void handleFuncOp(mlir::func::FuncOp funcOp
, mlir::LLVM::DIFileAttr fileAttr
,
73 mlir::LLVM::DICompileUnitAttr cuAttr
,
74 fir::DebugTypeGenerator
&typeGen
,
75 mlir::SymbolTable
*symbolTable
);
76 std::optional
<mlir::LLVM::DIModuleAttr
>
77 getModuleAttrFromGlobalOp(fir::GlobalOp globalOp
,
78 mlir::LLVM::DIFileAttr fileAttr
,
79 mlir::LLVM::DIScopeAttr scope
);
82 bool debugInfoIsAlreadySet(mlir::Location loc
) {
83 if (mlir::isa
<mlir::FusedLoc
>(loc
)) {
84 if (loc
->findInstanceOf
<mlir::FusedLocWith
<fir::LocationKindAttr
>>())
93 void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp
,
94 mlir::LLVM::DIFileAttr fileAttr
,
95 mlir::LLVM::DIScopeAttr scopeAttr
,
96 fir::DebugTypeGenerator
&typeGen
,
97 mlir::SymbolTable
*symbolTable
) {
98 mlir::MLIRContext
*context
= &getContext();
99 mlir::OpBuilder
builder(context
);
100 auto result
= fir::NameUniquer::deconstruct(declOp
.getUniqName());
102 if (result
.first
!= fir::NameUniquer::NameKind::VARIABLE
)
104 // If this DeclareOp actually represents a global then treat it as such.
105 if (auto global
= symbolTable
->lookup
<fir::GlobalOp
>(declOp
.getUniqName())) {
106 handleGlobalOp(global
, fileAttr
, scopeAttr
, typeGen
, symbolTable
, declOp
);
110 // Only accept local variables.
111 if (result
.second
.procs
.empty())
114 // FIXME: There may be cases where an argument is processed a bit before
115 // DeclareOp is generated. In that case, DeclareOp may point to an
116 // intermediate op and not to BlockArgument.
117 // Moreover, with MLIR inlining we cannot use the BlockArgument
118 // position to identify the original number of the dummy argument.
119 // If we want to keep running AddDebugInfoPass late, the dummy argument
120 // position in the argument list has to be expressed in FIR (e.g. as a
121 // constant attribute of [hl]fir.declare/fircg.ext_declare operation that has
122 // a dummy_scope operand).
124 if (declOp
.getDummyScope()) {
125 if (auto arg
= llvm::dyn_cast
<mlir::BlockArgument
>(declOp
.getMemref()))
126 argNo
= arg
.getArgNumber() + 1;
129 auto tyAttr
= typeGen
.convertType(fir::unwrapRefType(declOp
.getType()),
130 fileAttr
, scopeAttr
, declOp
);
132 auto localVarAttr
= mlir::LLVM::DILocalVariableAttr::get(
133 context
, scopeAttr
, mlir::StringAttr::get(context
, result
.second
.name
),
134 fileAttr
, getLineFromLoc(declOp
.getLoc()), argNo
, /* alignInBits*/ 0,
135 tyAttr
, mlir::LLVM::DIFlags::Zero
);
136 declOp
->setLoc(builder
.getFusedLoc({declOp
->getLoc()}, localVarAttr
));
139 // The `module` does not have a first class representation in the `FIR`. We
140 // extract information about it from the name of the identifiers and keep a
141 // map to avoid duplication.
142 mlir::LLVM::DIModuleAttr
AddDebugInfoPass::getOrCreateModuleAttr(
143 const std::string
&name
, mlir::LLVM::DIFileAttr fileAttr
,
144 mlir::LLVM::DIScopeAttr scope
, unsigned line
, bool decl
) {
145 mlir::MLIRContext
*context
= &getContext();
146 mlir::LLVM::DIModuleAttr modAttr
;
147 if (auto iter
{moduleMap
.find(name
)}; iter
!= moduleMap
.end()) {
148 modAttr
= iter
->getValue();
150 modAttr
= mlir::LLVM::DIModuleAttr::get(
151 context
, fileAttr
, scope
, mlir::StringAttr::get(context
, name
),
152 /* configMacros */ mlir::StringAttr(),
153 /* includePath */ mlir::StringAttr(),
154 /* apinotes */ mlir::StringAttr(), line
, decl
);
155 moduleMap
[name
] = modAttr
;
160 /// If globalOp represents a module variable, return a ModuleAttr that
161 /// represents that module.
162 std::optional
<mlir::LLVM::DIModuleAttr
>
163 AddDebugInfoPass::getModuleAttrFromGlobalOp(fir::GlobalOp globalOp
,
164 mlir::LLVM::DIFileAttr fileAttr
,
165 mlir::LLVM::DIScopeAttr scope
) {
166 mlir::MLIRContext
*context
= &getContext();
167 mlir::OpBuilder
builder(context
);
169 std::pair result
= fir::NameUniquer::deconstruct(globalOp
.getSymName());
170 // Only look for module if this variable is not part of a function.
171 if (!result
.second
.procs
.empty() || result
.second
.modules
.empty())
174 // DWARF5 says following about the fortran modules:
175 // A Fortran 90 module may also be represented by a module entry
176 // (but no declaration attribute is warranted because Fortran has no concept
177 // of a corresponding module body).
178 // But in practice, compilers use declaration attribute with a module in cases
179 // where module was defined in another source file (only being used in this
180 // one). The isInitialized() seems to provide the right information
181 // but inverted. It is true where module is actually defined but false where
183 // FIXME: Currently we don't have the line number on which a module was
184 // declared. We are using a best guess of line - 1 where line is the source
185 // line of the first member of the module that we encounter.
186 unsigned line
= getLineFromLoc(globalOp
.getLoc());
188 mlir::LLVM::DISubprogramAttr sp
=
189 mlir::dyn_cast_if_present
<mlir::LLVM::DISubprogramAttr
>(scope
);
190 // Modules are generated at compile unit scope
192 scope
= sp
.getCompileUnit();
194 return getOrCreateModuleAttr(result
.second
.modules
[0], fileAttr
, scope
,
195 std::max(line
- 1, (unsigned)1),
196 !globalOp
.isInitialized());
199 void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp
,
200 mlir::LLVM::DIFileAttr fileAttr
,
201 mlir::LLVM::DIScopeAttr scope
,
202 fir::DebugTypeGenerator
&typeGen
,
203 mlir::SymbolTable
*symbolTable
,
204 fir::cg::XDeclareOp declOp
) {
205 if (debugInfoIsAlreadySet(globalOp
.getLoc()))
207 mlir::MLIRContext
*context
= &getContext();
208 mlir::OpBuilder
builder(context
);
210 std::pair result
= fir::NameUniquer::deconstruct(globalOp
.getSymName());
211 if (result
.first
!= fir::NameUniquer::NameKind::VARIABLE
)
214 if (fir::NameUniquer::isSpecialSymbol(result
.second
.name
))
217 unsigned line
= getLineFromLoc(globalOp
.getLoc());
218 std::optional
<mlir::LLVM::DIModuleAttr
> modOpt
=
219 getModuleAttrFromGlobalOp(globalOp
, fileAttr
, scope
);
223 mlir::LLVM::DITypeAttr diType
=
224 typeGen
.convertType(globalOp
.getType(), fileAttr
, scope
, declOp
);
225 auto gvAttr
= mlir::LLVM::DIGlobalVariableAttr::get(
226 context
, scope
, mlir::StringAttr::get(context
, result
.second
.name
),
227 mlir::StringAttr::get(context
, globalOp
.getName()), fileAttr
, line
,
228 diType
, /*isLocalToUnit*/ false,
229 /*isDefinition*/ globalOp
.isInitialized(), /* alignInBits*/ 0);
230 globalOp
->setLoc(builder
.getFusedLoc({globalOp
->getLoc()}, gvAttr
));
233 void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp
,
234 mlir::LLVM::DIFileAttr fileAttr
,
235 mlir::LLVM::DICompileUnitAttr cuAttr
,
236 fir::DebugTypeGenerator
&typeGen
,
237 mlir::SymbolTable
*symbolTable
) {
238 mlir::Location l
= funcOp
->getLoc();
239 // If fused location has already been created then nothing to do
240 // Otherwise, create a fused location.
241 if (debugInfoIsAlreadySet(l
))
244 mlir::MLIRContext
*context
= &getContext();
245 mlir::OpBuilder
builder(context
);
246 llvm::StringRef
fileName(fileAttr
.getName());
247 llvm::StringRef
filePath(fileAttr
.getDirectory());
248 unsigned int CC
= (funcOp
.getName() == fir::NameUniquer::doProgramEntry())
249 ? llvm::dwarf::getCallingConvention("DW_CC_program")
250 : llvm::dwarf::getCallingConvention("DW_CC_normal");
252 if (auto funcLoc
= mlir::dyn_cast
<mlir::FileLineColLoc
>(l
)) {
253 fileName
= llvm::sys::path::filename(funcLoc
.getFilename().getValue());
254 filePath
= llvm::sys::path::parent_path(funcLoc
.getFilename().getValue());
257 mlir::StringAttr fullName
= mlir::StringAttr::get(context
, funcOp
.getName());
258 mlir::Attribute attr
= funcOp
->getAttr(fir::getInternalFuncNameAttrName());
259 mlir::StringAttr funcName
=
260 (attr
) ? mlir::cast
<mlir::StringAttr
>(attr
)
261 : mlir::StringAttr::get(context
, funcOp
.getName());
263 auto result
= fir::NameUniquer::deconstruct(funcName
);
264 funcName
= mlir::StringAttr::get(context
, result
.second
.name
);
266 // try to use a better function name than _QQmain for the program statement
268 if (funcName
== fir::NameUniquer::doProgramEntry()) {
270 mlir::StringAttr bindcName
=
271 funcOp
->getAttrOfType
<mlir::StringAttr
>(fir::getSymbolAttrName());
273 funcName
= bindcName
;
276 llvm::SmallVector
<mlir::LLVM::DITypeAttr
> types
;
277 for (auto resTy
: funcOp
.getResultTypes()) {
279 typeGen
.convertType(resTy
, fileAttr
, cuAttr
, /*declOp=*/nullptr);
280 types
.push_back(tyAttr
);
282 // If no return type then add a null type as a place holder for that.
284 types
.push_back(mlir::LLVM::DINullTypeAttr::get(context
));
285 for (auto inTy
: funcOp
.getArgumentTypes()) {
286 auto tyAttr
= typeGen
.convertType(fir::unwrapRefType(inTy
), fileAttr
,
287 cuAttr
, /*declOp=*/nullptr);
288 types
.push_back(tyAttr
);
291 mlir::LLVM::DISubroutineTypeAttr subTypeAttr
=
292 mlir::LLVM::DISubroutineTypeAttr::get(context
, CC
, types
);
293 mlir::LLVM::DIFileAttr funcFileAttr
=
294 mlir::LLVM::DIFileAttr::get(context
, fileName
, filePath
);
296 // Only definitions need a distinct identifier and a compilation unit.
297 mlir::DistinctAttr id
, id2
;
298 mlir::LLVM::DIScopeAttr Scope
= fileAttr
;
299 mlir::LLVM::DICompileUnitAttr compilationUnit
;
300 mlir::LLVM::DISubprogramFlags subprogramFlags
=
301 mlir::LLVM::DISubprogramFlags
{};
303 subprogramFlags
= mlir::LLVM::DISubprogramFlags::Optimized
;
306 subprogramFlags
| mlir::LLVM::DISubprogramFlags::MainSubprogram
;
307 if (!funcOp
.isExternal()) {
308 // Place holder and final function have to have different IDs, otherwise
309 // translation code will reject one of them.
310 id
= mlir::DistinctAttr::create(mlir::UnitAttr::get(context
));
311 id2
= mlir::DistinctAttr::create(mlir::UnitAttr::get(context
));
312 compilationUnit
= cuAttr
;
314 subprogramFlags
| mlir::LLVM::DISubprogramFlags::Definition
;
316 unsigned line
= getLineFromLoc(l
);
317 if (fir::isInternalProcedure(funcOp
)) {
318 // For contained functions, the scope is the parent subroutine.
319 mlir::SymbolRefAttr sym
= mlir::cast
<mlir::SymbolRefAttr
>(
320 funcOp
->getAttr(fir::getHostSymbolAttrName()));
323 symbolTable
->lookup
<mlir::func::FuncOp
>(sym
.getLeafReference())) {
324 // Make sure that parent is processed.
325 handleFuncOp(func
, fileAttr
, cuAttr
, typeGen
, symbolTable
);
327 mlir::dyn_cast_if_present
<mlir::FusedLoc
>(func
.getLoc())) {
329 mlir::dyn_cast_if_present
<mlir::LLVM::DISubprogramAttr
>(
330 fusedLoc
.getMetadata()))
335 } else if (!result
.second
.modules
.empty()) {
336 Scope
= getOrCreateModuleAttr(result
.second
.modules
[0], fileAttr
, cuAttr
,
340 // Don't process variables if user asked for line tables only.
341 if (debugLevel
== mlir::LLVM::DIEmissionKind::LineTablesOnly
) {
342 auto spAttr
= mlir::LLVM::DISubprogramAttr::get(
343 context
, id
, compilationUnit
, Scope
, funcName
, fullName
, funcFileAttr
,
344 line
, line
, subprogramFlags
, subTypeAttr
, /*retainedNodes=*/{},
346 funcOp
->setLoc(builder
.getFusedLoc({l
}, spAttr
));
350 mlir::DistinctAttr recId
=
351 mlir::DistinctAttr::create(mlir::UnitAttr::get(context
));
353 // The debug attribute in MLIR are readonly once created. But in case of
354 // imported entities, we have a circular dependency. The
355 // DIImportedEntityAttr requires scope information (DISubprogramAttr in this
356 // case) and DISubprogramAttr requires the list of imported entities. The
357 // MLIR provides a way where a DISubprogramAttr an be created with a certain
358 // recID and be used in places like DIImportedEntityAttr. After that another
359 // DISubprogramAttr can be created with same recID but with list of entities
360 // now available. The MLIR translation code takes care of updating the
361 // references. Note that references will be updated only in the things that
362 // are part of DISubprogramAttr (like DIImportedEntityAttr) so we have to
363 // create the final DISubprogramAttr before we process local variables.
364 // Look at DIRecursiveTypeAttrInterface for more details.
366 auto spAttr
= mlir::LLVM::DISubprogramAttr::get(
367 context
, recId
, /*isRecSelf=*/true, id
, compilationUnit
, Scope
, funcName
,
368 fullName
, funcFileAttr
, line
, line
, subprogramFlags
, subTypeAttr
,
369 /*retainedNodes=*/{}, /*annotations=*/{});
371 // There is no direct information in the IR for any 'use' statement in the
372 // function. We have to extract that information from the DeclareOp. We do
373 // a pass on the DeclareOp and generate ModuleAttr and corresponding
374 // DIImportedEntityAttr for that module.
375 // FIXME: As we are depending on the variables to see which module is being
376 // 'used' in the function, there are certain limitations.
377 // For things like 'use mod1, only: v1', whole module will be brought into the
378 // namespace in the debug info. It is not a problem as such unless there is a
380 // There is no information about module variable renaming
381 llvm::DenseSet
<mlir::LLVM::DIImportedEntityAttr
> importedModules
;
382 funcOp
.walk([&](fir::cg::XDeclareOp declOp
) {
383 if (&funcOp
.front() == declOp
->getBlock())
385 symbolTable
->lookup
<fir::GlobalOp
>(declOp
.getUniqName())) {
386 std::optional
<mlir::LLVM::DIModuleAttr
> modOpt
=
387 getModuleAttrFromGlobalOp(global
, fileAttr
, cuAttr
);
389 auto importedEntity
= mlir::LLVM::DIImportedEntityAttr::get(
390 context
, llvm::dwarf::DW_TAG_imported_module
, spAttr
, *modOpt
,
391 fileAttr
, /*line=*/1, /*name=*/nullptr, /*elements*/ {});
392 importedModules
.insert(importedEntity
);
396 llvm::SmallVector
<mlir::LLVM::DINodeAttr
> entities(importedModules
.begin(),
397 importedModules
.end());
398 // We have the imported entities now. Generate the final DISubprogramAttr.
399 spAttr
= mlir::LLVM::DISubprogramAttr::get(
400 context
, recId
, /*isRecSelf=*/false, id2
, compilationUnit
, Scope
,
401 funcName
, fullName
, funcFileAttr
, line
, line
, subprogramFlags
,
402 subTypeAttr
, entities
, /*annotations=*/{});
403 funcOp
->setLoc(builder
.getFusedLoc({l
}, spAttr
));
405 funcOp
.walk([&](fir::cg::XDeclareOp declOp
) {
406 // FIXME: We currently dont handle variables that are not in the entry
407 // blocks of the fuctions. These may be variable or arguments used in the
408 // OpenMP target regions.
409 if (&funcOp
.front() == declOp
->getBlock())
410 handleDeclareOp(declOp
, fileAttr
, spAttr
, typeGen
, symbolTable
);
414 void AddDebugInfoPass::runOnOperation() {
415 mlir::ModuleOp module
= getOperation();
416 mlir::MLIRContext
*context
= &getContext();
417 mlir::SymbolTable
symbolTable(module
);
418 llvm::StringRef fileName
;
419 std::string filePath
;
420 std::optional
<mlir::DataLayout
> dl
=
421 fir::support::getOrSetDataLayout(module
, /*allowDefaultLayout=*/true);
423 mlir::emitError(module
.getLoc(), "Missing data layout attribute in module");
427 fir::DebugTypeGenerator
typeGen(module
, &symbolTable
, *dl
);
428 // We need 2 type of file paths here.
429 // 1. Name of the file as was presented to compiler. This can be absolute
431 // 2. Current working directory
433 // We are also dealing with 2 different situations below. One is normal
434 // compilation where we will have a value in 'inputFilename' and we can
435 // obtain the current directory using 'current_path'.
436 // The 2nd case is when this pass is invoked directly from 'fir-opt' tool.
437 // In that case, 'inputFilename' may be empty. Location embedded in the
438 // module will be used to get file name and its directory.
439 if (inputFilename
.empty()) {
440 if (auto fileLoc
= mlir::dyn_cast
<mlir::FileLineColLoc
>(module
.getLoc())) {
441 fileName
= llvm::sys::path::filename(fileLoc
.getFilename().getValue());
442 filePath
= llvm::sys::path::parent_path(fileLoc
.getFilename().getValue());
446 fileName
= inputFilename
;
447 llvm::SmallString
<256> cwd
;
448 if (!llvm::sys::fs::current_path(cwd
))
449 filePath
= cwd
.str();
452 mlir::LLVM::DIFileAttr fileAttr
=
453 mlir::LLVM::DIFileAttr::get(context
, fileName
, filePath
);
454 mlir::StringAttr producer
=
455 mlir::StringAttr::get(context
, Fortran::common::getFlangFullVersion());
456 mlir::LLVM::DICompileUnitAttr cuAttr
= mlir::LLVM::DICompileUnitAttr::get(
457 mlir::DistinctAttr::create(mlir::UnitAttr::get(context
)),
458 llvm::dwarf::getLanguage("DW_LANG_Fortran95"), fileAttr
, producer
,
459 isOptimized
, debugLevel
);
461 module
.walk([&](mlir::func::FuncOp funcOp
) {
462 handleFuncOp(funcOp
, fileAttr
, cuAttr
, typeGen
, &symbolTable
);
464 // Process any global which was not processed through DeclareOp.
465 if (debugLevel
== mlir::LLVM::DIEmissionKind::Full
) {
466 // Process 'GlobalOp' only if full debug info is requested.
467 for (auto globalOp
: module
.getOps
<fir::GlobalOp
>())
468 handleGlobalOp(globalOp
, fileAttr
, cuAttr
, typeGen
, &symbolTable
,
473 std::unique_ptr
<mlir::Pass
>
474 fir::createAddDebugInfoPass(fir::AddDebugInfoOptions options
) {
475 return std::make_unique
<AddDebugInfoPass
>(options
);