brintos

brintos / llvm-project-archived public Read only

0
0
Text · 33.0 KiB · 7491b7b Raw
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