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