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/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 "mlir/Dialect/Func/IR/FuncOps.h" 26 #include "mlir/Dialect/LLVMIR/LLVMDialect.h" 27 #include "mlir/IR/Matchers.h" 28 #include "mlir/IR/TypeUtilities.h" 29 #include "mlir/Pass/Pass.h" 30 #include "mlir/Transforms/DialectConversion.h" 31 #include "mlir/Transforms/GreedyPatternRewriteDriver.h" 32 #include "mlir/Transforms/RegionUtils.h" 33 #include "llvm/BinaryFormat/Dwarf.h" 34 #include "llvm/Support/Debug.h" 35 #include "llvm/Support/FileSystem.h" 36 #include "llvm/Support/Path.h" 37 #include "llvm/Support/raw_ostream.h" 38 39 namespace fir { 40 #define GEN_PASS_DEF_ADDDEBUGINFO 41 #include "flang/Optimizer/Transforms/Passes.h.inc" 42 } // namespace fir 43 44 #define DEBUG_TYPE "flang-add-debug-info" 45 46 namespace { 47 48 class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> { 49 void handleDeclareOp(fir::cg::XDeclareOp declOp, 50 mlir::LLVM::DIFileAttr fileAttr, 51 mlir::LLVM::DIScopeAttr scopeAttr, 52 fir::DebugTypeGenerator &typeGen); 53 54 public: 55 AddDebugInfoPass(fir::AddDebugInfoOptions options) : Base(options) {} 56 void runOnOperation() override; 57 }; 58 59 static uint32_t getLineFromLoc(mlir::Location loc) { 60 uint32_t line = 1; 61 if (auto fileLoc = mlir::dyn_cast<mlir::FileLineColLoc>(loc)) 62 line = fileLoc.getLine(); 63 return line; 64 } 65 66 } // namespace 67 68 void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp, 69 mlir::LLVM::DIFileAttr fileAttr, 70 mlir::LLVM::DIScopeAttr scopeAttr, 71 fir::DebugTypeGenerator &typeGen) { 72 mlir::MLIRContext *context = &getContext(); 73 mlir::OpBuilder builder(context); 74 auto result = fir::NameUniquer::deconstruct(declOp.getUniqName()); 75 76 if (result.first != fir::NameUniquer::NameKind::VARIABLE) 77 return; 78 79 // Only accept local variables. 80 if (result.second.procs.empty()) 81 return; 82 83 // FIXME: There may be cases where an argument is processed a bit before 84 // DeclareOp is generated. In that case, DeclareOp may point to an 85 // intermediate op and not to BlockArgument. We need to find those cases and 86 // walk the chain to get to the actual argument. 87 88 unsigned argNo = 0; 89 if (auto Arg = llvm::dyn_cast<mlir::BlockArgument>(declOp.getMemref())) 90 argNo = Arg.getArgNumber() + 1; 91 92 auto tyAttr = typeGen.convertType(fir::unwrapRefType(declOp.getType()), 93 fileAttr, scopeAttr, declOp.getLoc()); 94 95 auto localVarAttr = mlir::LLVM::DILocalVariableAttr::get( 96 context, scopeAttr, mlir::StringAttr::get(context, result.second.name), 97 fileAttr, getLineFromLoc(declOp.getLoc()), argNo, /* alignInBits*/ 0, 98 tyAttr); 99 declOp->setLoc(builder.getFusedLoc({declOp->getLoc()}, localVarAttr)); 100 } 101 102 void AddDebugInfoPass::runOnOperation() { 103 mlir::ModuleOp module = getOperation(); 104 mlir::MLIRContext *context = &getContext(); 105 mlir::OpBuilder builder(context); 106 llvm::StringRef fileName; 107 std::string filePath; 108 // We need 2 type of file paths here. 109 // 1. Name of the file as was presented to compiler. This can be absolute 110 // or relative to 2. 111 // 2. Current working directory 112 // 113 // We are also dealing with 2 different situations below. One is normal 114 // compilation where we will have a value in 'inputFilename' and we can 115 // obtain the current directory using 'current_path'. 116 // The 2nd case is when this pass is invoked directly from 'fir-opt' tool. 117 // In that case, 'inputFilename' may be empty. Location embedded in the 118 // module will be used to get file name and its directory. 119 if (inputFilename.empty()) { 120 if (auto fileLoc = mlir::dyn_cast<mlir::FileLineColLoc>(module.getLoc())) { 121 fileName = llvm::sys::path::filename(fileLoc.getFilename().getValue()); 122 filePath = llvm::sys::path::parent_path(fileLoc.getFilename().getValue()); 123 } else 124 fileName = "-"; 125 } else { 126 fileName = inputFilename; 127 llvm::SmallString<256> cwd; 128 if (!llvm::sys::fs::current_path(cwd)) 129 filePath = cwd.str(); 130 } 131 132 mlir::LLVM::DIFileAttr fileAttr = 133 mlir::LLVM::DIFileAttr::get(context, fileName, filePath); 134 mlir::StringAttr producer = 135 mlir::StringAttr::get(context, Fortran::common::getFlangFullVersion()); 136 mlir::LLVM::DICompileUnitAttr cuAttr = mlir::LLVM::DICompileUnitAttr::get( 137 mlir::DistinctAttr::create(mlir::UnitAttr::get(context)), 138 llvm::dwarf::getLanguage("DW_LANG_Fortran95"), fileAttr, producer, 139 isOptimized, debugLevel); 140 141 module.walk([&](mlir::func::FuncOp funcOp) { 142 mlir::Location l = funcOp->getLoc(); 143 // If fused location has already been created then nothing to do 144 // Otherwise, create a fused location. 145 if (mlir::dyn_cast<mlir::FusedLoc>(l)) 146 return; 147 148 unsigned int CC = (funcOp.getName() == fir::NameUniquer::doProgramEntry()) 149 ? llvm::dwarf::getCallingConvention("DW_CC_program") 150 : llvm::dwarf::getCallingConvention("DW_CC_normal"); 151 152 if (auto funcLoc = mlir::dyn_cast<mlir::FileLineColLoc>(l)) { 153 fileName = llvm::sys::path::filename(funcLoc.getFilename().getValue()); 154 filePath = llvm::sys::path::parent_path(funcLoc.getFilename().getValue()); 155 } 156 157 mlir::StringAttr fullName = 158 mlir::StringAttr::get(context, funcOp.getName()); 159 auto result = fir::NameUniquer::deconstruct(funcOp.getName()); 160 mlir::StringAttr funcName = 161 mlir::StringAttr::get(context, result.second.name); 162 163 llvm::SmallVector<mlir::LLVM::DITypeAttr> types; 164 fir::DebugTypeGenerator typeGen(module); 165 for (auto resTy : funcOp.getResultTypes()) { 166 auto tyAttr = 167 typeGen.convertType(resTy, fileAttr, cuAttr, funcOp.getLoc()); 168 types.push_back(tyAttr); 169 } 170 for (auto inTy : funcOp.getArgumentTypes()) { 171 auto tyAttr = typeGen.convertType(fir::unwrapRefType(inTy), fileAttr, 172 cuAttr, funcOp.getLoc()); 173 types.push_back(tyAttr); 174 } 175 176 mlir::LLVM::DISubroutineTypeAttr subTypeAttr = 177 mlir::LLVM::DISubroutineTypeAttr::get(context, CC, types); 178 mlir::LLVM::DIFileAttr funcFileAttr = 179 mlir::LLVM::DIFileAttr::get(context, fileName, filePath); 180 181 // Only definitions need a distinct identifier and a compilation unit. 182 mlir::DistinctAttr id; 183 mlir::LLVM::DICompileUnitAttr compilationUnit; 184 mlir::LLVM::DISubprogramFlags subprogramFlags = 185 mlir::LLVM::DISubprogramFlags{}; 186 if (isOptimized) 187 subprogramFlags = mlir::LLVM::DISubprogramFlags::Optimized; 188 if (!funcOp.isExternal()) { 189 id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context)); 190 compilationUnit = cuAttr; 191 subprogramFlags = 192 subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition; 193 } 194 unsigned line = getLineFromLoc(l); 195 auto spAttr = mlir::LLVM::DISubprogramAttr::get( 196 context, id, compilationUnit, fileAttr, funcName, fullName, 197 funcFileAttr, line, line, subprogramFlags, subTypeAttr); 198 funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr)); 199 200 funcOp.walk([&](fir::cg::XDeclareOp declOp) { 201 handleDeclareOp(declOp, fileAttr, spAttr, typeGen); 202 }); 203 }); 204 } 205 206 std::unique_ptr<mlir::Pass> 207 fir::createAddDebugInfoPass(fir::AddDebugInfoOptions options) { 208 return std::make_unique<AddDebugInfoPass>(options); 209 } 210