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

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