1//===-------------- AddDebugInfo.cpp -- add debug info -------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9//===----------------------------------------------------------------------===//
10/// \file
11/// This pass populates some debug information for the module and functions.
12//===----------------------------------------------------------------------===//
13
14#include "DebugTypeGenerator.h"
15#include "flang/Optimizer/Builder/FIRBuilder.h"
16#include "flang/Optimizer/Builder/Todo.h"
17#include "flang/Optimizer/Dialect/FIRCG/CGOps.h"
18#include "flang/Optimizer/Dialect/FIRDialect.h"
19#include "flang/Optimizer/Dialect/FIROps.h"
20#include "flang/Optimizer/Dialect/FIROpsSupport.h"
21#include "flang/Optimizer/Dialect/FIRType.h"
22#include "flang/Optimizer/Dialect/Support/FIRContext.h"
23#include "flang/Optimizer/Support/InternalNames.h"
24#include "flang/Optimizer/Transforms/Passes.h"
25#include "flang/Support/Version.h"
26#include "mlir/Dialect/DLTI/DLTI.h"
27#include "mlir/Dialect/Func/IR/FuncOps.h"
28#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
29#include "mlir/IR/Matchers.h"
30#include "mlir/IR/TypeUtilities.h"
31#include "mlir/Pass/Pass.h"
32#include "mlir/Transforms/DialectConversion.h"
33#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
34#include "mlir/Transforms/RegionUtils.h"
35#include "llvm/BinaryFormat/Dwarf.h"
36#include "llvm/Support/Debug.h"
37#include "llvm/Support/FileSystem.h"
38#include "llvm/Support/Path.h"
39#include "llvm/Support/raw_ostream.h"
40
41namespace fir {
42#define GEN_PASS_DEF_ADDDEBUGINFO
43#include "flang/Optimizer/Transforms/Passes.h.inc"
44} // namespace fir
45
46#define DEBUG_TYPE "flang-add-debug-info"
47
48namespace {
49
50class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
51 void handleDeclareOp(fir::cg::XDeclareOp declOp,
52 mlir::LLVM::DIFileAttr fileAttr,
53 mlir::LLVM::DIScopeAttr scopeAttr,
54 fir::DebugTypeGenerator &typeGen,
55 mlir::SymbolTable *symbolTable);
56
57public:
58 AddDebugInfoPass(fir::AddDebugInfoOptions options) : Base(options) {}
59 void runOnOperation() override;
60
61private:
62 llvm::StringMap<mlir::LLVM::DIModuleAttr> moduleMap;
63 llvm::StringMap<mlir::LLVM::DICommonBlockAttr> commonBlockMap;
64 // List of GlobalVariableExpressionAttr that are attached to a given global
65 // that represents the storage for common block.
66 llvm::DenseMap<fir::GlobalOp, llvm::SmallVector<mlir::Attribute>>
67 globalToGlobalExprsMap;
68
69 mlir::LLVM::DIModuleAttr getOrCreateModuleAttr(
70 const std::string &name, mlir::LLVM::DIFileAttr fileAttr,
71 mlir::LLVM::DIScopeAttr scope, unsigned line, bool decl);
72 mlir::LLVM::DICommonBlockAttr
73 getOrCreateCommonBlockAttr(llvm::StringRef name,
74 mlir::LLVM::DIFileAttr fileAttr,
75 mlir::LLVM::DIScopeAttr scope, unsigned line);
76
77 void handleGlobalOp(fir::GlobalOp glocalOp, mlir::LLVM::DIFileAttr fileAttr,
78 mlir::LLVM::DIScopeAttr scope,
79 fir::DebugTypeGenerator &typeGen,
80 mlir::SymbolTable *symbolTable,
81 fir::cg::XDeclareOp declOp);
82 void handleFuncOp(mlir::func::FuncOp funcOp, mlir::LLVM::DIFileAttr fileAttr,
83 mlir::LLVM::DICompileUnitAttr cuAttr,
84 fir::DebugTypeGenerator &typeGen,
85 mlir::SymbolTable *symbolTable);
86 bool createCommonBlockGlobal(fir::cg::XDeclareOp declOp,
87 const std::string &name,
88 mlir::LLVM::DIFileAttr fileAttr,
89 mlir::LLVM::DIScopeAttr scopeAttr,
90 fir::DebugTypeGenerator &typeGen,
91 mlir::SymbolTable *symbolTable);
92 std::optional<mlir::LLVM::DIModuleAttr>
93 getModuleAttrFromGlobalOp(fir::GlobalOp globalOp,
94 mlir::LLVM::DIFileAttr fileAttr,
95 mlir::LLVM::DIScopeAttr scope);
96};
97
98bool debugInfoIsAlreadySet(mlir::Location loc) {
99 if (mlir::isa<mlir::FusedLoc>(loc)) {
100 if (loc->findInstanceOf<mlir::FusedLocWith<fir::LocationKindAttr>>())
101 return false;
102 return true;
103 }
104 return false;
105}
106
107} // namespace
108
109bool AddDebugInfoPass::createCommonBlockGlobal(
110 fir::cg::XDeclareOp declOp, const std::string &name,
111 mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DIScopeAttr scopeAttr,
112 fir::DebugTypeGenerator &typeGen, mlir::SymbolTable *symbolTable) {
113 mlir::MLIRContext *context = &getContext();
114 mlir::OpBuilder builder(context);
115 std::optional<std::int64_t> optint;
116 mlir::Operation *op = declOp.getMemref().getDefiningOp();
117
118 if (auto conOp = mlir::dyn_cast_if_present<fir::ConvertOp>(op))
119 op = conOp.getValue().getDefiningOp();
120
121 if (auto cordOp = mlir::dyn_cast_if_present<fir::CoordinateOp>(op)) {
122 auto coors = cordOp.getCoor();
123 if (coors.size() != 1)
124 return false;
125 optint = fir::getIntIfConstant(coors[0]);
126 if (!optint)
127 return false;
128 op = cordOp.getRef().getDefiningOp();
129 if (auto conOp2 = mlir::dyn_cast_if_present<fir::ConvertOp>(op))
130 op = conOp2.getValue().getDefiningOp();
131
132 if (auto addrOfOp = mlir::dyn_cast_if_present<fir::AddrOfOp>(op)) {
133 mlir::SymbolRefAttr sym = addrOfOp.getSymbol();
134 if (auto global =
135 symbolTable->lookup<fir::GlobalOp>(sym.getRootReference())) {
136
137 unsigned line = getLineFromLoc(global.getLoc());
138 llvm::StringRef commonName(sym.getRootReference());
139 // FIXME: We are trying to extract the name of the common block from the
140 // name of the global. As part of mangling, GetCommonBlockObjectName can
141 // add a trailing _ in the name of that global. The demangle function
142 // does not seem to handle such cases. So the following hack is used to
143 // remove the trailing '_'.
144 if (commonName != Fortran::common::blankCommonObjectName &&
145 commonName.back() == '_')
146 commonName = commonName.drop_back();
147 mlir::LLVM::DICommonBlockAttr commonBlock =
148 getOrCreateCommonBlockAttr(commonName, fileAttr, scopeAttr, line);
149 mlir::LLVM::DITypeAttr diType = typeGen.convertType(
150 fir::unwrapRefType(declOp.getType()), fileAttr, scopeAttr, declOp);
151 line = getLineFromLoc(declOp.getLoc());
152 auto gvAttr = mlir::LLVM::DIGlobalVariableAttr::get(
153 context, commonBlock, mlir::StringAttr::get(context, name),
154 declOp.getUniqName(), fileAttr, line, diType,
155 /*isLocalToUnit*/ false, /*isDefinition*/ true, /* alignInBits*/ 0);
156 mlir::LLVM::DIExpressionAttr expr;
157 if (*optint != 0) {
158 llvm::SmallVector<mlir::LLVM::DIExpressionElemAttr> ops;
159 ops.push_back(mlir::LLVM::DIExpressionElemAttr::get(
160 context, llvm::dwarf::DW_OP_plus_uconst, *optint));
161 expr = mlir::LLVM::DIExpressionAttr::get(context, ops);
162 }
163 auto dbgExpr = mlir::LLVM::DIGlobalVariableExpressionAttr::get(
164 global.getContext(), gvAttr, expr);
165 globalToGlobalExprsMap[global].push_back(dbgExpr);
166 return true;
167 }
168 }
169 }
170 return false;
171}
172
173void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,
174 mlir::LLVM::DIFileAttr fileAttr,
175 mlir::LLVM::DIScopeAttr scopeAttr,
176 fir::DebugTypeGenerator &typeGen,
177 mlir::SymbolTable *symbolTable) {
178 mlir::MLIRContext *context = &getContext();
179 mlir::OpBuilder builder(context);
180 auto result = fir::NameUniquer::deconstruct(declOp.getUniqName());
181
182 if (result.first != fir::NameUniquer::NameKind::VARIABLE)
183 return;
184
185 if (createCommonBlockGlobal(declOp, result.second.name, fileAttr, scopeAttr,
186 typeGen, symbolTable))
187 return;
188
189 // If this DeclareOp actually represents a global then treat it as such.
190 mlir::Operation *defOp = declOp.getMemref().getDefiningOp();
191 if (defOp && llvm::isa<fir::AddrOfOp>(defOp)) {
192 if (auto global =
193 symbolTable->lookup<fir::GlobalOp>(declOp.getUniqName())) {
194 handleGlobalOp(global, fileAttr, scopeAttr, typeGen, symbolTable, declOp);
195 return;
196 }
197 }
198
199 // FIXME: There may be cases where an argument is processed a bit before
200 // DeclareOp is generated. In that case, DeclareOp may point to an
201 // intermediate op and not to BlockArgument.
202 // Moreover, with MLIR inlining we cannot use the BlockArgument
203 // position to identify the original number of the dummy argument.
204 // If we want to keep running AddDebugInfoPass late, the dummy argument
205 // position in the argument list has to be expressed in FIR (e.g. as a
206 // constant attribute of [hl]fir.declare/fircg.ext_declare operation that has
207 // a dummy_scope operand).
208 unsigned argNo = 0;
209 if (declOp.getDummyScope()) {
210 if (auto arg = llvm::dyn_cast<mlir::BlockArgument>(declOp.getMemref())) {
211 // Check if it is the BlockArgument of the function's entry block.
212 if (auto funcLikeOp =
213 declOp->getParentOfType<mlir::FunctionOpInterface>())
214 if (arg.getOwner() == &funcLikeOp.front())
215 argNo = arg.getArgNumber() + 1;
216 }
217 }
218
219 auto tyAttr = typeGen.convertType(fir::unwrapRefType(declOp.getType()),
220 fileAttr, scopeAttr, declOp);
221
222 auto localVarAttr = mlir::LLVM::DILocalVariableAttr::get(
223 context, scopeAttr, mlir::StringAttr::get(context, result.second.name),
224 fileAttr, getLineFromLoc(declOp.getLoc()), argNo, /* alignInBits*/ 0,
225 tyAttr, mlir::LLVM::DIFlags::Zero);
226 declOp->setLoc(builder.getFusedLoc({declOp->getLoc()}, localVarAttr));
227}
228
229mlir::LLVM::DICommonBlockAttr AddDebugInfoPass::getOrCreateCommonBlockAttr(
230 llvm::StringRef name, mlir::LLVM::DIFileAttr fileAttr,
231 mlir::LLVM::DIScopeAttr scope, unsigned line) {
232 mlir::MLIRContext *context = &getContext();
233 mlir::LLVM::DICommonBlockAttr cbAttr;
234 if (auto iter{commonBlockMap.find(name)}; iter != commonBlockMap.end()) {
235 cbAttr = iter->getValue();
236 } else {
237 cbAttr = mlir::LLVM::DICommonBlockAttr::get(
238 context, scope, nullptr, mlir::StringAttr::get(context, name), fileAttr,
239 line);
240 commonBlockMap[name] = cbAttr;
241 }
242 return cbAttr;
243}
244
245// The `module` does not have a first class representation in the `FIR`. We
246// extract information about it from the name of the identifiers and keep a
247// map to avoid duplication.
248mlir::LLVM::DIModuleAttr AddDebugInfoPass::getOrCreateModuleAttr(
249 const std::string &name, mlir::LLVM::DIFileAttr fileAttr,
250 mlir::LLVM::DIScopeAttr scope, unsigned line, bool decl) {
251 mlir::MLIRContext *context = &getContext();
252 mlir::LLVM::DIModuleAttr modAttr;
253 if (auto iter{moduleMap.find(name)}; iter != moduleMap.end()) {
254 modAttr = iter->getValue();
255 } else {
256 modAttr = mlir::LLVM::DIModuleAttr::get(
257 context, fileAttr, scope, mlir::StringAttr::get(context, name),
258 /* configMacros */ mlir::StringAttr(),
259 /* includePath */ mlir::StringAttr(),
260 /* apinotes */ mlir::StringAttr(), line, decl);
261 moduleMap[name] = modAttr;
262 }
263 return modAttr;
264}
265
266/// If globalOp represents a module variable, return a ModuleAttr that
267/// represents that module.
268std::optional<mlir::LLVM::DIModuleAttr>
269AddDebugInfoPass::getModuleAttrFromGlobalOp(fir::GlobalOp globalOp,
270 mlir::LLVM::DIFileAttr fileAttr,
271 mlir::LLVM::DIScopeAttr scope) {
272 mlir::MLIRContext *context = &getContext();
273 mlir::OpBuilder builder(context);
274
275 std::pair result = fir::NameUniquer::deconstruct(globalOp.getSymName());
276 // Only look for module if this variable is not part of a function.
277 if (!result.second.procs.empty() || result.second.modules.empty())
278 return std::nullopt;
279
280 // DWARF5 says following about the fortran modules:
281 // A Fortran 90 module may also be represented by a module entry
282 // (but no declaration attribute is warranted because Fortran has no concept
283 // of a corresponding module body).
284 // But in practice, compilers use declaration attribute with a module in cases
285 // where module was defined in another source file (only being used in this
286 // one). The isInitialized() seems to provide the right information
287 // but inverted. It is true where module is actually defined but false where
288 // it is used.
289 // FIXME: Currently we don't have the line number on which a module was
290 // declared. We are using a best guess of line - 1 where line is the source
291 // line of the first member of the module that we encounter.
292 unsigned line = getLineFromLoc(globalOp.getLoc());
293
294 mlir::LLVM::DISubprogramAttr sp =
295 mlir::dyn_cast_if_present<mlir::LLVM::DISubprogramAttr>(scope);
296 // Modules are generated at compile unit scope
297 if (sp)
298 scope = sp.getCompileUnit();
299
300 return getOrCreateModuleAttr(result.second.modules[0], fileAttr, scope,
301 std::max(line - 1, (unsigned)1),
302 !globalOp.isInitialized());
303}
304
305void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
306 mlir::LLVM::DIFileAttr fileAttr,
307 mlir::LLVM::DIScopeAttr scope,
308 fir::DebugTypeGenerator &typeGen,
309 mlir::SymbolTable *symbolTable,
310 fir::cg::XDeclareOp declOp) {
311 if (debugInfoIsAlreadySet(globalOp.getLoc()))
312 return;
313 mlir::MLIRContext *context = &getContext();
314 mlir::OpBuilder builder(context);
315
316 std::pair result = fir::NameUniquer::deconstruct(globalOp.getSymName());
317 if (result.first != fir::NameUniquer::NameKind::VARIABLE)
318 return;
319
320 if (fir::NameUniquer::isSpecialSymbol(result.second.name))
321 return;
322
323 unsigned line = getLineFromLoc(globalOp.getLoc());
324 std::optional<mlir::LLVM::DIModuleAttr> modOpt =
325 getModuleAttrFromGlobalOp(globalOp, fileAttr, scope);
326 if (modOpt)
327 scope = *modOpt;
328
329 mlir::LLVM::DITypeAttr diType =
330 typeGen.convertType(globalOp.getType(), fileAttr, scope, declOp);
331 auto gvAttr = mlir::LLVM::DIGlobalVariableAttr::get(
332 context, scope, mlir::StringAttr::get(context, result.second.name),
333 mlir::StringAttr::get(context, globalOp.getName()), fileAttr, line,
334 diType, /*isLocalToUnit*/ false,
335 /*isDefinition*/ globalOp.isInitialized(), /* alignInBits*/ 0);
336 auto dbgExpr = mlir::LLVM::DIGlobalVariableExpressionAttr::get(
337 globalOp.getContext(), gvAttr, nullptr);
338 auto arrayAttr = mlir::ArrayAttr::get(context, {dbgExpr});
339 globalOp->setLoc(builder.getFusedLoc({globalOp.getLoc()}, arrayAttr));
340}
341
342void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
343 mlir::LLVM::DIFileAttr fileAttr,
344 mlir::LLVM::DICompileUnitAttr cuAttr,
345 fir::DebugTypeGenerator &typeGen,
346 mlir::SymbolTable *symbolTable) {
347 mlir::Location l = funcOp->getLoc();
348 // If fused location has already been created then nothing to do
349 // Otherwise, create a fused location.
350 if (debugInfoIsAlreadySet(l))
351 return;
352
353 mlir::MLIRContext *context = &getContext();
354 mlir::OpBuilder builder(context);
355 llvm::StringRef fileName(fileAttr.getName());
356 llvm::StringRef filePath(fileAttr.getDirectory());
357 unsigned int CC = (funcOp.getName() == fir::NameUniquer::doProgramEntry())
358 ? llvm::dwarf::getCallingConvention("DW_CC_program")
359 : llvm::dwarf::getCallingConvention("DW_CC_normal");
360
361 if (auto funcLoc = mlir::dyn_cast<mlir::FileLineColLoc>(l)) {
362 fileName = llvm::sys::path::filename(path: funcLoc.getFilename().getValue());
363 filePath = llvm::sys::path::parent_path(path: funcLoc.getFilename().getValue());
364 }
365
366 mlir::StringAttr fullName = mlir::StringAttr::get(context, funcOp.getName());
367 mlir::Attribute attr = funcOp->getAttr(fir::getInternalFuncNameAttrName());
368 mlir::StringAttr funcName =
369 (attr) ? mlir::cast<mlir::StringAttr>(attr)
370 : mlir::StringAttr::get(context, funcOp.getName());
371
372 auto result = fir::NameUniquer::deconstruct(funcName);
373 funcName = mlir::StringAttr::get(context, result.second.name);
374
375 // try to use a better function name than _QQmain for the program statement
376 bool isMain = false;
377 if (funcName == fir::NameUniquer::doProgramEntry()) {
378 isMain = true;
379 mlir::StringAttr bindcName =
380 funcOp->getAttrOfType<mlir::StringAttr>(fir::getSymbolAttrName());
381 if (bindcName)
382 funcName = bindcName;
383 }
384
385 llvm::SmallVector<mlir::LLVM::DITypeAttr> types;
386 for (auto resTy : funcOp.getResultTypes()) {
387 auto tyAttr =
388 typeGen.convertType(resTy, fileAttr, cuAttr, /*declOp=*/nullptr);
389 types.push_back(tyAttr);
390 }
391 // If no return type then add a null type as a place holder for that.
392 if (types.empty())
393 types.push_back(mlir::LLVM::DINullTypeAttr::get(context));
394 for (auto inTy : funcOp.getArgumentTypes()) {
395 auto tyAttr = typeGen.convertType(fir::unwrapRefType(inTy), fileAttr,
396 cuAttr, /*declOp=*/nullptr);
397 types.push_back(tyAttr);
398 }
399
400 mlir::LLVM::DISubroutineTypeAttr subTypeAttr =
401 mlir::LLVM::DISubroutineTypeAttr::get(context, CC, types);
402 mlir::LLVM::DIFileAttr funcFileAttr =
403 mlir::LLVM::DIFileAttr::get(context, fileName, filePath);
404
405 // Only definitions need a distinct identifier and a compilation unit.
406 mlir::DistinctAttr id, id2;
407 mlir::LLVM::DIScopeAttr Scope = fileAttr;
408 mlir::LLVM::DICompileUnitAttr compilationUnit;
409 mlir::LLVM::DISubprogramFlags subprogramFlags =
410 mlir::LLVM::DISubprogramFlags{};
411 if (isOptimized)
412 subprogramFlags = mlir::LLVM::DISubprogramFlags::Optimized;
413 if (isMain)
414 subprogramFlags =
415 subprogramFlags | mlir::LLVM::DISubprogramFlags::MainSubprogram;
416 if (!funcOp.isExternal()) {
417 // Place holder and final function have to have different IDs, otherwise
418 // translation code will reject one of them.
419 id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
420 id2 = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
421 compilationUnit = cuAttr;
422 subprogramFlags =
423 subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;
424 }
425 unsigned line = getLineFromLoc(l);
426 if (fir::isInternalProcedure(funcOp)) {
427 // For contained functions, the scope is the parent subroutine.
428 mlir::SymbolRefAttr sym = mlir::cast<mlir::SymbolRefAttr>(
429 funcOp->getAttr(fir::getHostSymbolAttrName()));
430 if (sym) {
431 if (auto func =
432 symbolTable->lookup<mlir::func::FuncOp>(sym.getLeafReference())) {
433 // Make sure that parent is processed.
434 handleFuncOp(func, fileAttr, cuAttr, typeGen, symbolTable);
435 if (auto fusedLoc =
436 mlir::dyn_cast_if_present<mlir::FusedLoc>(func.getLoc())) {
437 if (auto spAttr =
438 mlir::dyn_cast_if_present<mlir::LLVM::DISubprogramAttr>(
439 fusedLoc.getMetadata()))
440 Scope = spAttr;
441 }
442 }
443 }
444 } else if (!result.second.modules.empty()) {
445 Scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, cuAttr,
446 line - 1, false);
447 }
448
449 // Don't process variables if user asked for line tables only.
450 if (debugLevel == mlir::LLVM::DIEmissionKind::LineTablesOnly) {
451 auto spAttr = mlir::LLVM::DISubprogramAttr::get(
452 context, id, compilationUnit, Scope, funcName, fullName, funcFileAttr,
453 line, line, subprogramFlags, subTypeAttr, /*retainedNodes=*/{},
454 /*annotations=*/{});
455 funcOp->setLoc(builder.getFusedLoc({l}, spAttr));
456 return;
457 }
458
459 mlir::DistinctAttr recId =
460 mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
461
462 // The debug attribute in MLIR are readonly once created. But in case of
463 // imported entities, we have a circular dependency. The
464 // DIImportedEntityAttr requires scope information (DISubprogramAttr in this
465 // case) and DISubprogramAttr requires the list of imported entities. The
466 // MLIR provides a way where a DISubprogramAttr an be created with a certain
467 // recID and be used in places like DIImportedEntityAttr. After that another
468 // DISubprogramAttr can be created with same recID but with list of entities
469 // now available. The MLIR translation code takes care of updating the
470 // references. Note that references will be updated only in the things that
471 // are part of DISubprogramAttr (like DIImportedEntityAttr) so we have to
472 // create the final DISubprogramAttr before we process local variables.
473 // Look at DIRecursiveTypeAttrInterface for more details.
474
475 auto spAttr = mlir::LLVM::DISubprogramAttr::get(
476 context, recId, /*isRecSelf=*/true, id, compilationUnit, Scope, funcName,
477 fullName, funcFileAttr, line, line, subprogramFlags, subTypeAttr,
478 /*retainedNodes=*/{}, /*annotations=*/{});
479
480 // There is no direct information in the IR for any 'use' statement in the
481 // function. We have to extract that information from the DeclareOp. We do
482 // a pass on the DeclareOp and generate ModuleAttr and corresponding
483 // DIImportedEntityAttr for that module.
484 // FIXME: As we are depending on the variables to see which module is being
485 // 'used' in the function, there are certain limitations.
486 // For things like 'use mod1, only: v1', whole module will be brought into the
487 // namespace in the debug info. It is not a problem as such unless there is a
488 // clash of names.
489 // There is no information about module variable renaming
490 llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> importedModules;
491 funcOp.walk([&](fir::cg::XDeclareOp declOp) {
492 if (&funcOp.front() == declOp->getBlock())
493 if (auto global =
494 symbolTable->lookup<fir::GlobalOp>(declOp.getUniqName())) {
495 std::optional<mlir::LLVM::DIModuleAttr> modOpt =
496 getModuleAttrFromGlobalOp(global, fileAttr, cuAttr);
497 if (modOpt) {
498 auto importedEntity = mlir::LLVM::DIImportedEntityAttr::get(
499 context, llvm::dwarf::DW_TAG_imported_module, spAttr, *modOpt,
500 fileAttr, /*line=*/1, /*name=*/nullptr, /*elements*/ {});
501 importedModules.insert(importedEntity);
502 }
503 }
504 });
505 llvm::SmallVector<mlir::LLVM::DINodeAttr> entities(importedModules.begin(),
506 importedModules.end());
507 // We have the imported entities now. Generate the final DISubprogramAttr.
508 spAttr = mlir::LLVM::DISubprogramAttr::get(
509 context, recId, /*isRecSelf=*/false, id2, compilationUnit, Scope,
510 funcName, fullName, funcFileAttr, line, line, subprogramFlags,
511 subTypeAttr, entities, /*annotations=*/{});
512 funcOp->setLoc(builder.getFusedLoc({l}, spAttr));
513
514 funcOp.walk([&](fir::cg::XDeclareOp declOp) {
515 handleDeclareOp(declOp, fileAttr, spAttr, typeGen, symbolTable);
516 });
517 // commonBlockMap ensures that we don't create multiple DICommonBlockAttr of
518 // the same name in one function. But it is ok (rather required) to create
519 // them in different functions if common block of the same name has been used
520 // there.
521 commonBlockMap.clear();
522}
523
524void AddDebugInfoPass::runOnOperation() {
525 mlir::ModuleOp module = getOperation();
526 mlir::MLIRContext *context = &getContext();
527 mlir::SymbolTable symbolTable(module);
528 llvm::StringRef fileName;
529 std::string filePath;
530 std::optional<mlir::DataLayout> dl =
531 fir::support::getOrSetMLIRDataLayout(module, /*allowDefaultLayout=*/true);
532 if (!dl) {
533 mlir::emitError(module.getLoc(), "Missing data layout attribute in module");
534 signalPassFailure();
535 return;
536 }
537 fir::DebugTypeGenerator typeGen(module, &symbolTable, *dl);
538 // We need 2 type of file paths here.
539 // 1. Name of the file as was presented to compiler. This can be absolute
540 // or relative to 2.
541 // 2. Current working directory
542 //
543 // We are also dealing with 2 different situations below. One is normal
544 // compilation where we will have a value in 'inputFilename' and we can
545 // obtain the current directory using 'current_path'.
546 // The 2nd case is when this pass is invoked directly from 'fir-opt' tool.
547 // In that case, 'inputFilename' may be empty. Location embedded in the
548 // module will be used to get file name and its directory.
549 if (inputFilename.empty()) {
550 if (auto fileLoc = mlir::dyn_cast<mlir::FileLineColLoc>(module.getLoc())) {
551 fileName = llvm::sys::path::filename(path: fileLoc.getFilename().getValue());
552 filePath = llvm::sys::path::parent_path(path: fileLoc.getFilename().getValue());
553 } else
554 fileName = "-";
555 } else {
556 fileName = inputFilename;
557 llvm::SmallString<256> cwd;
558 if (!llvm::sys::fs::current_path(result&: cwd))
559 filePath = cwd.str();
560 }
561
562 mlir::LLVM::DIFileAttr fileAttr =
563 mlir::LLVM::DIFileAttr::get(context, fileName, filePath);
564 mlir::StringAttr producer =
565 mlir::StringAttr::get(context, Fortran::common::getFlangFullVersion());
566 mlir::LLVM::DICompileUnitAttr cuAttr = mlir::LLVM::DICompileUnitAttr::get(
567 mlir::DistinctAttr::create(mlir::UnitAttr::get(context)),
568 llvm::dwarf::getLanguage("DW_LANG_Fortran95"), fileAttr, producer,
569 isOptimized, debugLevel);
570
571 module.walk([&](mlir::func::FuncOp funcOp) {
572 handleFuncOp(funcOp, fileAttr, cuAttr, typeGen, &symbolTable);
573 });
574 mlir::OpBuilder builder(context);
575 // We have processed all function. Attach common block variables to the
576 // global that represent the storage.
577 for (auto [global, exprs] : globalToGlobalExprsMap) {
578 auto arrayAttr = mlir::ArrayAttr::get(context, exprs);
579 global->setLoc(builder.getFusedLoc({global.getLoc()}, arrayAttr));
580 }
581 // Process any global which was not processed through DeclareOp.
582 if (debugLevel == mlir::LLVM::DIEmissionKind::Full) {
583 // Process 'GlobalOp' only if full debug info is requested.
584 for (auto globalOp : module.getOps<fir::GlobalOp>())
585 handleGlobalOp(globalOp, fileAttr, cuAttr, typeGen, &symbolTable,
586 /*declOp=*/nullptr);
587 }
588}
589
590std::unique_ptr<mlir::Pass>
591fir::createAddDebugInfoPass(fir::AddDebugInfoOptions options) {
592 return std::make_unique<AddDebugInfoPass>(options);
593}
594

source code of flang/lib/Optimizer/Transforms/AddDebugInfo.cpp