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 63 mlir::LLVM::DIModuleAttr getOrCreateModuleAttr( 64 const std::string &name, mlir::LLVM::DIFileAttr fileAttr, 65 mlir::LLVM::DIScopeAttr scope, unsigned line, bool decl); 66 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); 80 }; 81 82 bool debugInfoIsAlreadySet(mlir::Location loc) { 83 if (mlir::isa<mlir::FusedLoc>(loc)) { 84 if (loc->findInstanceOf<mlir::FusedLocWith<fir::LocationKindAttr>>()) 85 return false; 86 return true; 87 } 88 return false; 89 } 90 91 } // namespace 92 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()); 101 102 if (result.first != fir::NameUniquer::NameKind::VARIABLE) 103 return; 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); 107 return; 108 } 109 110 // Only accept local variables. 111 if (result.second.procs.empty()) 112 return; 113 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). 123 unsigned argNo = 0; 124 if (fir::isDummyArgument(declOp.getMemref())) { 125 auto arg = llvm::cast<mlir::BlockArgument>(declOp.getMemref()); 126 argNo = arg.getArgNumber() + 1; 127 } 128 129 auto tyAttr = typeGen.convertType(fir::unwrapRefType(declOp.getType()), 130 fileAttr, scopeAttr, declOp); 131 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)); 137 } 138 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(); 149 } else { 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; 156 } 157 return modAttr; 158 } 159 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); 168 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()) 172 return std::nullopt; 173 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 182 // it is used. 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()); 187 188 mlir::LLVM::DISubprogramAttr sp = 189 mlir::dyn_cast_if_present<mlir::LLVM::DISubprogramAttr>(scope); 190 // Modules are generated at compile unit scope 191 if (sp) 192 scope = sp.getCompileUnit(); 193 194 return getOrCreateModuleAttr(result.second.modules[0], fileAttr, scope, 195 std::max(line - 1, (unsigned)1), 196 !globalOp.isInitialized()); 197 } 198 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())) 206 return; 207 mlir::MLIRContext *context = &getContext(); 208 mlir::OpBuilder builder(context); 209 210 std::pair result = fir::NameUniquer::deconstruct(globalOp.getSymName()); 211 if (result.first != fir::NameUniquer::NameKind::VARIABLE) 212 return; 213 214 if (fir::NameUniquer::isSpecialSymbol(result.second.name)) 215 return; 216 217 unsigned line = getLineFromLoc(globalOp.getLoc()); 218 std::optional<mlir::LLVM::DIModuleAttr> modOpt = 219 getModuleAttrFromGlobalOp(globalOp, fileAttr, scope); 220 if (modOpt) 221 scope = *modOpt; 222 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)); 231 } 232 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)) 242 return; 243 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"); 251 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()); 255 } 256 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()); 262 263 auto result = fir::NameUniquer::deconstruct(funcName); 264 funcName = mlir::StringAttr::get(context, result.second.name); 265 266 // try to use a better function name than _QQmain for the program statement 267 bool isMain = false; 268 if (funcName == fir::NameUniquer::doProgramEntry()) { 269 isMain = true; 270 mlir::StringAttr bindcName = 271 funcOp->getAttrOfType<mlir::StringAttr>(fir::getSymbolAttrName()); 272 if (bindcName) 273 funcName = bindcName; 274 } 275 276 llvm::SmallVector<mlir::LLVM::DITypeAttr> types; 277 for (auto resTy : funcOp.getResultTypes()) { 278 auto tyAttr = 279 typeGen.convertType(resTy, fileAttr, cuAttr, /*declOp=*/nullptr); 280 types.push_back(tyAttr); 281 } 282 // If no return type then add a null type as a place holder for that. 283 if (types.empty()) 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); 289 } 290 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); 295 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{}; 302 if (isOptimized) 303 subprogramFlags = mlir::LLVM::DISubprogramFlags::Optimized; 304 if (isMain) 305 subprogramFlags = 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; 313 subprogramFlags = 314 subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition; 315 } 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())); 321 if (sym) { 322 if (auto func = 323 symbolTable->lookup<mlir::func::FuncOp>(sym.getLeafReference())) { 324 // Make sure that parent is processed. 325 handleFuncOp(func, fileAttr, cuAttr, typeGen, symbolTable); 326 if (auto fusedLoc = 327 mlir::dyn_cast_if_present<mlir::FusedLoc>(func.getLoc())) { 328 if (auto spAttr = 329 mlir::dyn_cast_if_present<mlir::LLVM::DISubprogramAttr>( 330 fusedLoc.getMetadata())) 331 Scope = spAttr; 332 } 333 } 334 } 335 } else if (!result.second.modules.empty()) { 336 Scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, cuAttr, 337 line - 1, false); 338 } 339 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=*/{}, 345 /*annotations=*/{}); 346 funcOp->setLoc(builder.getFusedLoc({l}, spAttr)); 347 return; 348 } 349 350 mlir::DistinctAttr recId = 351 mlir::DistinctAttr::create(mlir::UnitAttr::get(context)); 352 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. 365 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=*/{}); 370 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 379 // clash of names. 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()) 384 if (auto global = 385 symbolTable->lookup<fir::GlobalOp>(declOp.getUniqName())) { 386 std::optional<mlir::LLVM::DIModuleAttr> modOpt = 387 getModuleAttrFromGlobalOp(global, fileAttr, cuAttr); 388 if (modOpt) { 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); 393 } 394 } 395 }); 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)); 404 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); 411 }); 412 } 413 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); 422 if (!dl) { 423 mlir::emitError(module.getLoc(), "Missing data layout attribute in module"); 424 signalPassFailure(); 425 return; 426 } 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 430 // or relative to 2. 431 // 2. Current working directory 432 // 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()); 443 } else 444 fileName = "-"; 445 } else { 446 fileName = inputFilename; 447 llvm::SmallString<256> cwd; 448 if (!llvm::sys::fs::current_path(cwd)) 449 filePath = cwd.str(); 450 } 451 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); 460 461 module.walk([&](mlir::func::FuncOp funcOp) { 462 handleFuncOp(funcOp, fileAttr, cuAttr, typeGen, &symbolTable); 463 }); 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, 469 /*declOp=*/nullptr); 470 } 471 } 472 473 std::unique_ptr<mlir::Pass> 474 fir::createAddDebugInfoPass(fir::AddDebugInfoOptions options) { 475 return std::make_unique<AddDebugInfoPass>(options); 476 } 477