xref: /llvm-project/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp (revision 61da6366d043792d7db280ce9edd2db62516e0e8)
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