750 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9//===----------------------------------------------------------------------===//10/// \file11/// 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_ADDDEBUGINFO44#include "flang/Optimizer/Transforms/Passes.h.inc"45} // namespace fir46 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, mlir::Value dummyScope);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 global66 // 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::DICommonBlockAttr74 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 to109// generate for omp::TargetOp. Its logic is borrowed from110// getTargetEntryUniqueInfo and111// TargetRegionEntryInfo::getTargetRegionEntryFnName to generate the same name.112// But even if there was a slight mismatch, it is not a problem because this113// 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(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} // namespace140 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 148 std::optional<std::int64_t> offset;149 mlir::Value storage = declOp.getStorage();150 if (!storage)151 return false;152 153 // Extract offset from storage_offset attribute154 uint64_t storageOffset = declOp.getStorageOffset();155 if (storageOffset != 0)156 offset = static_cast<std::int64_t>(storageOffset);157 158 // Get the GlobalOp from the storage value.159 // The storage may be wrapped in ConvertOp, so unwrap it first.160 mlir::Operation *storageOp = storage.getDefiningOp();161 if (auto convertOp = mlir::dyn_cast_if_present<fir::ConvertOp>(storageOp))162 storageOp = convertOp.getValue().getDefiningOp();163 164 auto addrOfOp = mlir::dyn_cast_if_present<fir::AddrOfOp>(storageOp);165 if (!addrOfOp)166 return false;167 168 mlir::SymbolRefAttr sym = addrOfOp.getSymbol();169 fir::GlobalOp global =170 symbolTable->lookup<fir::GlobalOp>(sym.getRootReference());171 if (!global)172 return false;173 174 // Check if the global is actually a common block by demangling its name.175 // Module EQUIVALENCE variables also use storage operands but are mangled176 // as VARIABLE type, so we reject them to avoid treating them as common177 // blocks.178 llvm::StringRef globalSymbol = sym.getRootReference();179 auto globalResult = fir::NameUniquer::deconstruct(globalSymbol);180 if (globalResult.first == fir::NameUniquer::NameKind::VARIABLE)181 return false;182 183 // FIXME: We are trying to extract the name of the common block from the184 // name of the global. As part of mangling, GetCommonBlockObjectName can185 // add a trailing _ in the name of that global. The demangle function186 // does not seem to handle such cases. So the following hack is used to187 // remove the trailing '_'.188 llvm::StringRef commonName = globalSymbol;189 if (commonName != Fortran::common::blankCommonObjectName &&190 !commonName.empty() && commonName.back() == '_')191 commonName = commonName.drop_back();192 193 // Create the debug attributes.194 unsigned line = getLineFromLoc(global.getLoc());195 mlir::LLVM::DICommonBlockAttr commonBlock =196 getOrCreateCommonBlockAttr(commonName, fileAttr, scopeAttr, line);197 198 mlir::LLVM::DITypeAttr diType = typeGen.convertType(199 fir::unwrapRefType(declOp.getType()), fileAttr, scopeAttr, declOp);200 201 line = getLineFromLoc(declOp.getLoc());202 auto gvAttr = mlir::LLVM::DIGlobalVariableAttr::get(203 context, commonBlock, mlir::StringAttr::get(context, name),204 declOp.getUniqName(), fileAttr, line, diType,205 /*isLocalToUnit*/ false, /*isDefinition*/ true, /* alignInBits*/ 0);206 207 // Create DIExpression for offset if needed208 mlir::LLVM::DIExpressionAttr expr;209 if (offset && *offset != 0) {210 llvm::SmallVector<mlir::LLVM::DIExpressionElemAttr> ops;211 ops.push_back(mlir::LLVM::DIExpressionElemAttr::get(212 context, llvm::dwarf::DW_OP_plus_uconst, *offset));213 expr = mlir::LLVM::DIExpressionAttr::get(context, ops);214 }215 216 auto dbgExpr = mlir::LLVM::DIGlobalVariableExpressionAttr::get(217 global.getContext(), gvAttr, expr);218 globalToGlobalExprsMap[global].push_back(dbgExpr);219 220 return true;221}222 223void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,224 mlir::LLVM::DIFileAttr fileAttr,225 mlir::LLVM::DIScopeAttr scopeAttr,226 fir::DebugTypeGenerator &typeGen,227 mlir::SymbolTable *symbolTable,228 mlir::Value dummyScope) {229 mlir::MLIRContext *context = &getContext();230 mlir::OpBuilder builder(context);231 auto result = fir::NameUniquer::deconstruct(declOp.getUniqName());232 233 if (result.first != fir::NameUniquer::NameKind::VARIABLE)234 return;235 236 if (createCommonBlockGlobal(declOp, result.second.name, fileAttr, scopeAttr,237 typeGen, symbolTable))238 return;239 240 // If this DeclareOp actually represents a global then treat it as such.241 mlir::Operation *defOp = declOp.getMemref().getDefiningOp();242 if (defOp && llvm::isa<fir::AddrOfOp>(defOp)) {243 if (auto global =244 symbolTable->lookup<fir::GlobalOp>(declOp.getUniqName())) {245 handleGlobalOp(global, fileAttr, scopeAttr, typeGen, symbolTable, declOp);246 return;247 }248 }249 250 // Get the dummy argument position from the explicit attribute.251 unsigned argNo = 0;252 if (dummyScope && declOp.getDummyScope() == dummyScope) {253 if (auto argNoOpt = declOp.getDummyArgNo())254 argNo = *argNoOpt;255 }256 257 auto tyAttr = typeGen.convertType(fir::unwrapRefType(declOp.getType()),258 fileAttr, scopeAttr, declOp);259 260 auto localVarAttr = mlir::LLVM::DILocalVariableAttr::get(261 context, scopeAttr, mlir::StringAttr::get(context, result.second.name),262 fileAttr, getLineFromLoc(declOp.getLoc()), argNo, /* alignInBits*/ 0,263 tyAttr, mlir::LLVM::DIFlags::Zero);264 declOp->setLoc(builder.getFusedLoc({declOp->getLoc()}, localVarAttr));265}266 267mlir::LLVM::DICommonBlockAttr AddDebugInfoPass::getOrCreateCommonBlockAttr(268 llvm::StringRef name, mlir::LLVM::DIFileAttr fileAttr,269 mlir::LLVM::DIScopeAttr scope, unsigned line) {270 mlir::MLIRContext *context = &getContext();271 mlir::LLVM::DICommonBlockAttr cbAttr;272 if (auto iter{commonBlockMap.find(name)}; iter != commonBlockMap.end()) {273 cbAttr = iter->getValue();274 } else {275 cbAttr = mlir::LLVM::DICommonBlockAttr::get(276 context, scope, nullptr, mlir::StringAttr::get(context, name), fileAttr,277 line);278 commonBlockMap[name] = cbAttr;279 }280 return cbAttr;281}282 283// The `module` does not have a first class representation in the `FIR`. We284// extract information about it from the name of the identifiers and keep a285// map to avoid duplication.286mlir::LLVM::DIModuleAttr AddDebugInfoPass::getOrCreateModuleAttr(287 const std::string &name, mlir::LLVM::DIFileAttr fileAttr,288 mlir::LLVM::DIScopeAttr scope, unsigned line, bool decl) {289 mlir::MLIRContext *context = &getContext();290 mlir::LLVM::DIModuleAttr modAttr;291 if (auto iter{moduleMap.find(name)}; iter != moduleMap.end()) {292 modAttr = iter->getValue();293 } else {294 // When decl is true, it means that module is only being used in this295 // compilation unit and it is defined elsewhere. But if the file/line/scope296 // fields are valid, the module is not merged with its definition and is297 // considered different. So we only set those fields when decl is false.298 modAttr = mlir::LLVM::DIModuleAttr::get(299 context, decl ? nullptr : fileAttr, decl ? nullptr : scope,300 mlir::StringAttr::get(context, name),301 /* configMacros */ mlir::StringAttr(),302 /* includePath */ mlir::StringAttr(),303 /* apinotes */ mlir::StringAttr(), decl ? 0 : line, decl);304 moduleMap[name] = modAttr;305 }306 return modAttr;307}308 309/// If globalOp represents a module variable, return a ModuleAttr that310/// represents that module.311std::optional<mlir::LLVM::DIModuleAttr>312AddDebugInfoPass::getModuleAttrFromGlobalOp(fir::GlobalOp globalOp,313 mlir::LLVM::DIFileAttr fileAttr,314 mlir::LLVM::DIScopeAttr scope) {315 mlir::MLIRContext *context = &getContext();316 mlir::OpBuilder builder(context);317 318 std::pair result = fir::NameUniquer::deconstruct(globalOp.getSymName());319 // Only look for module if this variable is not part of a function.320 if (!result.second.procs.empty() || result.second.modules.empty())321 return std::nullopt;322 323 // DWARF5 says following about the fortran modules:324 // A Fortran 90 module may also be represented by a module entry325 // (but no declaration attribute is warranted because Fortran has no concept326 // of a corresponding module body).327 // But in practice, compilers use declaration attribute with a module in cases328 // where module was defined in another source file (only being used in this329 // one). The isInitialized() seems to provide the right information330 // but inverted. It is true where module is actually defined but false where331 // it is used.332 // FIXME: Currently we don't have the line number on which a module was333 // declared. We are using a best guess of line - 1 where line is the source334 // line of the first member of the module that we encounter.335 unsigned line = getLineFromLoc(globalOp.getLoc());336 337 mlir::LLVM::DISubprogramAttr sp =338 mlir::dyn_cast_if_present<mlir::LLVM::DISubprogramAttr>(scope);339 // Modules are generated at compile unit scope340 if (sp)341 scope = sp.getCompileUnit();342 343 return getOrCreateModuleAttr(result.second.modules[0], fileAttr, scope,344 std::max(line - 1, (unsigned)1),345 !globalOp.isInitialized());346}347 348void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,349 mlir::LLVM::DIFileAttr fileAttr,350 mlir::LLVM::DIScopeAttr scope,351 fir::DebugTypeGenerator &typeGen,352 mlir::SymbolTable *symbolTable,353 fir::cg::XDeclareOp declOp) {354 if (debugInfoIsAlreadySet(globalOp.getLoc()))355 return;356 mlir::MLIRContext *context = &getContext();357 mlir::OpBuilder builder(context);358 359 std::pair result = fir::NameUniquer::deconstruct(globalOp.getSymName());360 if (result.first != fir::NameUniquer::NameKind::VARIABLE)361 return;362 363 if (fir::NameUniquer::isSpecialSymbol(result.second.name))364 return;365 366 unsigned line = getLineFromLoc(globalOp.getLoc());367 std::optional<mlir::LLVM::DIModuleAttr> modOpt =368 getModuleAttrFromGlobalOp(globalOp, fileAttr, scope);369 if (modOpt)370 scope = *modOpt;371 372 mlir::LLVM::DITypeAttr diType =373 typeGen.convertType(globalOp.getType(), fileAttr, scope, declOp);374 auto gvAttr = mlir::LLVM::DIGlobalVariableAttr::get(375 context, scope, mlir::StringAttr::get(context, result.second.name),376 mlir::StringAttr::get(context, globalOp.getName()), fileAttr, line,377 diType, /*isLocalToUnit*/ false,378 /*isDefinition*/ globalOp.isInitialized(), /* alignInBits*/ 0);379 auto dbgExpr = mlir::LLVM::DIGlobalVariableExpressionAttr::get(380 globalOp.getContext(), gvAttr, nullptr);381 auto arrayAttr = mlir::ArrayAttr::get(context, {dbgExpr});382 globalOp->setLoc(builder.getFusedLoc({globalOp.getLoc()}, arrayAttr));383}384 385void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,386 mlir::LLVM::DIFileAttr fileAttr,387 mlir::LLVM::DICompileUnitAttr cuAttr,388 fir::DebugTypeGenerator &typeGen,389 mlir::SymbolTable *symbolTable) {390 mlir::Location l = funcOp->getLoc();391 // If fused location has already been created then nothing to do392 // Otherwise, create a fused location.393 if (debugInfoIsAlreadySet(l))394 return;395 396 mlir::MLIRContext *context = &getContext();397 mlir::OpBuilder builder(context);398 llvm::StringRef fileName(fileAttr.getName());399 llvm::StringRef filePath(fileAttr.getDirectory());400 unsigned int CC = (funcOp.getName() == fir::NameUniquer::doProgramEntry())401 ? llvm::dwarf::getCallingConvention("DW_CC_program")402 : llvm::dwarf::getCallingConvention("DW_CC_normal");403 404 if (auto funcLoc = mlir::dyn_cast<mlir::FileLineColLoc>(l)) {405 fileName = llvm::sys::path::filename(funcLoc.getFilename().getValue());406 filePath = llvm::sys::path::parent_path(funcLoc.getFilename().getValue());407 }408 409 mlir::StringAttr fullName = mlir::StringAttr::get(context, funcOp.getName());410 mlir::Attribute attr = funcOp->getAttr(fir::getInternalFuncNameAttrName());411 mlir::StringAttr funcName =412 (attr) ? mlir::cast<mlir::StringAttr>(attr)413 : mlir::StringAttr::get(context, funcOp.getName());414 415 auto result = fir::NameUniquer::deconstruct(funcName);416 funcName = mlir::StringAttr::get(context, result.second.name);417 418 // try to use a better function name than _QQmain for the program statement419 bool isMain = false;420 if (funcName == fir::NameUniquer::doProgramEntry()) {421 isMain = true;422 mlir::StringAttr bindcName =423 funcOp->getAttrOfType<mlir::StringAttr>(fir::getSymbolAttrName());424 if (bindcName)425 funcName = bindcName;426 }427 428 llvm::SmallVector<mlir::LLVM::DITypeAttr> types;429 for (auto resTy : funcOp.getResultTypes()) {430 auto tyAttr =431 typeGen.convertType(resTy, fileAttr, cuAttr, /*declOp=*/nullptr);432 types.push_back(tyAttr);433 }434 // If no return type then add a null type as a place holder for that.435 if (types.empty())436 types.push_back(mlir::LLVM::DINullTypeAttr::get(context));437 for (auto inTy : funcOp.getArgumentTypes()) {438 auto tyAttr = typeGen.convertType(fir::unwrapRefType(inTy), fileAttr,439 cuAttr, /*declOp=*/nullptr);440 types.push_back(tyAttr);441 }442 443 mlir::LLVM::DISubroutineTypeAttr subTypeAttr =444 mlir::LLVM::DISubroutineTypeAttr::get(context, CC, types);445 mlir::LLVM::DIFileAttr funcFileAttr =446 mlir::LLVM::DIFileAttr::get(context, fileName, filePath);447 448 // Only definitions need a distinct identifier and a compilation unit.449 mlir::DistinctAttr id, id2;450 mlir::LLVM::DIScopeAttr Scope = fileAttr;451 mlir::LLVM::DICompileUnitAttr compilationUnit;452 mlir::LLVM::DISubprogramFlags subprogramFlags =453 mlir::LLVM::DISubprogramFlags{};454 if (isOptimized)455 subprogramFlags = mlir::LLVM::DISubprogramFlags::Optimized;456 if (isMain)457 subprogramFlags =458 subprogramFlags | mlir::LLVM::DISubprogramFlags::MainSubprogram;459 if (!funcOp.isExternal()) {460 // Place holder and final function have to have different IDs, otherwise461 // translation code will reject one of them.462 id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));463 id2 = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));464 compilationUnit = cuAttr;465 subprogramFlags =466 subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;467 }468 unsigned line = getLineFromLoc(l);469 if (fir::isInternalProcedure(funcOp)) {470 // For contained functions, the scope is the parent subroutine.471 mlir::SymbolRefAttr sym = mlir::cast<mlir::SymbolRefAttr>(472 funcOp->getAttr(fir::getHostSymbolAttrName()));473 if (sym) {474 if (auto func =475 symbolTable->lookup<mlir::func::FuncOp>(sym.getLeafReference())) {476 // Make sure that parent is processed.477 handleFuncOp(func, fileAttr, cuAttr, typeGen, symbolTable);478 if (auto fusedLoc =479 mlir::dyn_cast_if_present<mlir::FusedLoc>(func.getLoc())) {480 if (auto spAttr =481 mlir::dyn_cast_if_present<mlir::LLVM::DISubprogramAttr>(482 fusedLoc.getMetadata()))483 Scope = spAttr;484 }485 }486 }487 } else if (!result.second.modules.empty()) {488 Scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, cuAttr,489 line - 1, false);490 }491 492 auto addTargetOpDISP = [&](bool lineTableOnly,493 llvm::ArrayRef<mlir::LLVM::DINodeAttr> entities) {494 // When we process the DeclareOp inside the OpenMP target region, all the495 // variables get the DISubprogram of the parent function of the target op as496 // the scope. In the codegen (to llvm ir), OpenMP target op results in the497 // creation of a separate function. As the variables in the debug info have498 // the DISubprogram of the parent function as the scope, the variables499 // need to be updated at codegen time to avoid verification failures.500 501 // This updating after the fact becomes more and more difficult when types502 // are dependent on local variables like in the case of variable size arrays503 // or string. We not only have to generate new variables but also new types.504 // We can avoid this problem by generating a DISubprogramAttr here for the505 // target op and make sure that all the variables inside the target region506 // get the correct scope in the first place.507 funcOp.walk([&](mlir::omp::TargetOp targetOp) {508 unsigned line = getLineFromLoc(targetOp.getLoc());509 mlir::StringAttr name =510 getTargetFunctionName(context, targetOp.getLoc(), funcOp.getName());511 mlir::LLVM::DISubprogramFlags flags =512 mlir::LLVM::DISubprogramFlags::Definition |513 mlir::LLVM::DISubprogramFlags::LocalToUnit;514 if (isOptimized)515 flags = flags | mlir::LLVM::DISubprogramFlags::Optimized;516 517 mlir::DistinctAttr id =518 mlir::DistinctAttr::create(mlir::UnitAttr::get(context));519 llvm::SmallVector<mlir::LLVM::DITypeAttr> types;520 types.push_back(mlir::LLVM::DINullTypeAttr::get(context));521 for (auto arg : targetOp.getRegion().getArguments()) {522 auto tyAttr = typeGen.convertType(fir::unwrapRefType(arg.getType()),523 fileAttr, cuAttr, /*declOp=*/nullptr);524 types.push_back(tyAttr);525 }526 CC = llvm::dwarf::getCallingConvention("DW_CC_normal");527 mlir::LLVM::DISubroutineTypeAttr spTy =528 mlir::LLVM::DISubroutineTypeAttr::get(context, CC, types);529 if (lineTableOnly) {530 auto spAttr = mlir::LLVM::DISubprogramAttr::get(531 context, id, compilationUnit, Scope, name, name, funcFileAttr, line,532 line, flags, spTy, /*retainedNodes=*/{}, /*annotations=*/{});533 targetOp->setLoc(builder.getFusedLoc({targetOp.getLoc()}, spAttr));534 return;535 }536 mlir::DistinctAttr recId =537 mlir::DistinctAttr::create(mlir::UnitAttr::get(context));538 auto spAttr = mlir::LLVM::DISubprogramAttr::get(539 context, recId, /*isRecSelf=*/true, id, compilationUnit, Scope, name,540 name, funcFileAttr, line, line, flags, spTy, /*retainedNodes=*/{},541 /*annotations=*/{});542 543 // Make sure that information about the imported modules is copied in the544 // new function.545 llvm::SmallVector<mlir::LLVM::DINodeAttr> opEntities;546 for (mlir::LLVM::DINodeAttr N : entities) {547 if (auto entity = mlir::dyn_cast<mlir::LLVM::DIImportedEntityAttr>(N)) {548 auto importedEntity = mlir::LLVM::DIImportedEntityAttr::get(549 context, llvm::dwarf::DW_TAG_imported_module, spAttr,550 entity.getEntity(), fileAttr, /*line=*/1, /*name=*/nullptr,551 /*elements*/ {});552 opEntities.push_back(importedEntity);553 }554 }555 556 id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));557 spAttr = mlir::LLVM::DISubprogramAttr::get(558 context, recId, /*isRecSelf=*/false, id, compilationUnit, Scope, name,559 name, funcFileAttr, line, line, flags, spTy, opEntities,560 /*annotations=*/{});561 targetOp->setLoc(builder.getFusedLoc({targetOp.getLoc()}, spAttr));562 });563 };564 565 // Don't process variables if user asked for line tables only.566 if (debugLevel == mlir::LLVM::DIEmissionKind::LineTablesOnly) {567 auto spAttr = mlir::LLVM::DISubprogramAttr::get(568 context, id, compilationUnit, Scope, funcName, fullName, funcFileAttr,569 line, line, subprogramFlags, subTypeAttr, /*retainedNodes=*/{},570 /*annotations=*/{});571 funcOp->setLoc(builder.getFusedLoc({l}, spAttr));572 addTargetOpDISP(/*lineTableOnly=*/true, /*entities=*/{});573 return;574 }575 576 mlir::DistinctAttr recId =577 mlir::DistinctAttr::create(mlir::UnitAttr::get(context));578 579 // The debug attribute in MLIR are readonly once created. But in case of580 // imported entities, we have a circular dependency. The581 // DIImportedEntityAttr requires scope information (DISubprogramAttr in this582 // case) and DISubprogramAttr requires the list of imported entities. The583 // MLIR provides a way where a DISubprogramAttr an be created with a certain584 // recID and be used in places like DIImportedEntityAttr. After that another585 // DISubprogramAttr can be created with same recID but with list of entities586 // now available. The MLIR translation code takes care of updating the587 // references. Note that references will be updated only in the things that588 // are part of DISubprogramAttr (like DIImportedEntityAttr) so we have to589 // create the final DISubprogramAttr before we process local variables.590 // Look at DIRecursiveTypeAttrInterface for more details.591 592 auto spAttr = mlir::LLVM::DISubprogramAttr::get(593 context, recId, /*isRecSelf=*/true, id, compilationUnit, Scope, funcName,594 fullName, funcFileAttr, line, line, subprogramFlags, subTypeAttr,595 /*retainedNodes=*/{}, /*annotations=*/{});596 597 // There is no direct information in the IR for any 'use' statement in the598 // function. We have to extract that information from the DeclareOp. We do599 // a pass on the DeclareOp and generate ModuleAttr and corresponding600 // DIImportedEntityAttr for that module.601 // FIXME: As we are depending on the variables to see which module is being602 // 'used' in the function, there are certain limitations.603 // For things like 'use mod1, only: v1', whole module will be brought into the604 // namespace in the debug info. It is not a problem as such unless there is a605 // clash of names.606 // There is no information about module variable renaming607 llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> importedModules;608 funcOp.walk([&](fir::cg::XDeclareOp declOp) {609 if (&funcOp.front() == declOp->getBlock())610 if (auto global =611 symbolTable->lookup<fir::GlobalOp>(declOp.getUniqName())) {612 std::optional<mlir::LLVM::DIModuleAttr> modOpt =613 getModuleAttrFromGlobalOp(global, fileAttr, cuAttr);614 if (modOpt) {615 auto importedEntity = mlir::LLVM::DIImportedEntityAttr::get(616 context, llvm::dwarf::DW_TAG_imported_module, spAttr, *modOpt,617 fileAttr, /*line=*/1, /*name=*/nullptr, /*elements*/ {});618 importedModules.insert(importedEntity);619 }620 }621 });622 llvm::SmallVector<mlir::LLVM::DINodeAttr> entities(importedModules.begin(),623 importedModules.end());624 // We have the imported entities now. Generate the final DISubprogramAttr.625 spAttr = mlir::LLVM::DISubprogramAttr::get(626 context, recId, /*isRecSelf=*/false, id2, compilationUnit, Scope,627 funcName, fullName, funcFileAttr, line, line, subprogramFlags,628 subTypeAttr, entities, /*annotations=*/{});629 funcOp->setLoc(builder.getFusedLoc({l}, spAttr));630 addTargetOpDISP(/*lineTableOnly=*/false, entities);631 632 // Find the first dummy_scope definition. This is the one of the current633 // function. The other ones may come from inlined calls. The variables inside634 // those inlined calls should not be identified as arguments of the current635 // function.636 mlir::Value dummyScope;637 funcOp.walk([&](fir::UndefOp undef) -> mlir::WalkResult {638 // TODO: delay fir.dummy_scope translation to undefined until639 // codegeneration. This is nicer and safer to match.640 if (llvm::isa<fir::DummyScopeType>(undef.getType())) {641 dummyScope = undef;642 return mlir::WalkResult::interrupt();643 }644 return mlir::WalkResult::advance();645 });646 647 funcOp.walk([&](fir::cg::XDeclareOp declOp) {648 mlir::LLVM::DISubprogramAttr spTy = spAttr;649 if (auto tOp = declOp->getParentOfType<mlir::omp::TargetOp>()) {650 if (auto fusedLoc = llvm::dyn_cast<mlir::FusedLoc>(tOp.getLoc())) {651 if (auto sp = llvm::dyn_cast<mlir::LLVM::DISubprogramAttr>(652 fusedLoc.getMetadata()))653 spTy = sp;654 }655 }656 handleDeclareOp(declOp, fileAttr, spTy, typeGen, symbolTable, dummyScope);657 });658 // commonBlockMap ensures that we don't create multiple DICommonBlockAttr of659 // the same name in one function. But it is ok (rather required) to create660 // them in different functions if common block of the same name has been used661 // there.662 commonBlockMap.clear();663}664 665void AddDebugInfoPass::runOnOperation() {666 mlir::ModuleOp module = getOperation();667 mlir::MLIRContext *context = &getContext();668 mlir::SymbolTable symbolTable(module);669 llvm::StringRef fileName;670 std::string filePath;671 std::optional<mlir::DataLayout> dl =672 fir::support::getOrSetMLIRDataLayout(module, /*allowDefaultLayout=*/true);673 if (!dl) {674 mlir::emitError(module.getLoc(), "Missing data layout attribute in module");675 signalPassFailure();676 return;677 }678 mlir::OpBuilder builder(context);679 if (dwarfVersion > 0) {680 mlir::OpBuilder::InsertionGuard guard(builder);681 builder.setInsertionPointToEnd(module.getBody());682 llvm::SmallVector<mlir::Attribute> moduleFlags;683 mlir::IntegerType int32Ty = mlir::IntegerType::get(context, 32);684 moduleFlags.push_back(builder.getAttr<mlir::LLVM::ModuleFlagAttr>(685 mlir::LLVM::ModFlagBehavior::Max,686 mlir::StringAttr::get(context, "Dwarf Version"),687 mlir::IntegerAttr::get(int32Ty, dwarfVersion)));688 mlir::LLVM::ModuleFlagsOp::create(builder, module.getLoc(),689 builder.getArrayAttr(moduleFlags));690 }691 fir::DebugTypeGenerator typeGen(module, &symbolTable, *dl);692 // We need 2 type of file paths here.693 // 1. Name of the file as was presented to compiler. This can be absolute694 // or relative to 2.695 // 2. Current working directory696 //697 // We are also dealing with 2 different situations below. One is normal698 // compilation where we will have a value in 'inputFilename' and we can699 // obtain the current directory using 'current_path'.700 // The 2nd case is when this pass is invoked directly from 'fir-opt' tool.701 // In that case, 'inputFilename' may be empty. Location embedded in the702 // module will be used to get file name and its directory.703 if (inputFilename.empty()) {704 if (auto fileLoc = mlir::dyn_cast<mlir::FileLineColLoc>(module.getLoc())) {705 fileName = llvm::sys::path::filename(fileLoc.getFilename().getValue());706 filePath = llvm::sys::path::parent_path(fileLoc.getFilename().getValue());707 } else708 fileName = "-";709 } else {710 fileName = inputFilename;711 llvm::SmallString<256> cwd;712 if (!llvm::sys::fs::current_path(cwd))713 filePath = cwd.str();714 }715 716 mlir::LLVM::DIFileAttr fileAttr =717 mlir::LLVM::DIFileAttr::get(context, fileName, filePath);718 mlir::StringAttr producer =719 mlir::StringAttr::get(context, Fortran::common::getFlangFullVersion());720 mlir::LLVM::DICompileUnitAttr cuAttr = mlir::LLVM::DICompileUnitAttr::get(721 mlir::DistinctAttr::create(mlir::UnitAttr::get(context)),722 llvm::dwarf::getLanguage("DW_LANG_Fortran95"), fileAttr, producer,723 isOptimized, debugLevel,724 /*nameTableKind=*/mlir::LLVM::DINameTableKind::Default,725 splitDwarfFile.empty() ? mlir::StringAttr()726 : mlir::StringAttr::get(context, splitDwarfFile));727 728 module.walk([&](mlir::func::FuncOp funcOp) {729 handleFuncOp(funcOp, fileAttr, cuAttr, typeGen, &symbolTable);730 });731 // We have processed all function. Attach common block variables to the732 // global that represent the storage.733 for (auto [global, exprs] : globalToGlobalExprsMap) {734 auto arrayAttr = mlir::ArrayAttr::get(context, exprs);735 global->setLoc(builder.getFusedLoc({global.getLoc()}, arrayAttr));736 }737 // Process any global which was not processed through DeclareOp.738 if (debugLevel == mlir::LLVM::DIEmissionKind::Full) {739 // Process 'GlobalOp' only if full debug info is requested.740 for (auto globalOp : module.getOps<fir::GlobalOp>())741 handleGlobalOp(globalOp, fileAttr, cuAttr, typeGen, &symbolTable,742 /*declOp=*/nullptr);743 }744}745 746std::unique_ptr<mlir::Pass>747fir::createAddDebugInfoPass(fir::AddDebugInfoOptions options) {748 return std::make_unique<AddDebugInfoPass>(options);749}750