xref: /llvm-project/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp (revision 52cb9537896992e0fa689c45469bccb36970c0c7)
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/Dialect/FIRDialect.h"
19 #include "flang/Optimizer/Dialect/FIROps.h"
20 #include "flang/Optimizer/Dialect/FIRType.h"
21 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
22 #include "flang/Optimizer/Support/InternalNames.h"
23 #include "flang/Optimizer/Transforms/Passes.h"
24 #include "mlir/Dialect/Func/IR/FuncOps.h"
25 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
26 #include "mlir/IR/Matchers.h"
27 #include "mlir/IR/TypeUtilities.h"
28 #include "mlir/Pass/Pass.h"
29 #include "mlir/Transforms/DialectConversion.h"
30 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
31 #include "mlir/Transforms/RegionUtils.h"
32 #include "llvm/BinaryFormat/Dwarf.h"
33 #include "llvm/Support/Debug.h"
34 #include "llvm/Support/FileSystem.h"
35 #include "llvm/Support/Path.h"
36 #include "llvm/Support/raw_ostream.h"
37 
38 namespace fir {
39 #define GEN_PASS_DEF_ADDDEBUGINFO
40 #include "flang/Optimizer/Transforms/Passes.h.inc"
41 } // namespace fir
42 
43 #define DEBUG_TYPE "flang-add-debug-info"
44 
45 namespace {
46 
47 class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
48 public:
49   AddDebugInfoPass(fir::AddDebugInfoOptions options) : Base(options) {}
50   void runOnOperation() override;
51 };
52 
53 } // namespace
54 
55 void AddDebugInfoPass::runOnOperation() {
56   mlir::ModuleOp module = getOperation();
57   mlir::MLIRContext *context = &getContext();
58   mlir::OpBuilder builder(context);
59   llvm::StringRef fileName;
60   std::string filePath;
61   // We need 2 type of file paths here.
62   // 1. Name of the file as was presented to compiler. This can be absolute
63   // or relative to 2.
64   // 2. Current working directory
65   //
66   // We are also dealing with 2 different situations below. One is normal
67   // compilation where we will have a value in 'inputFilename' and we can
68   // obtain the current directory using 'current_path'.
69   // The 2nd case is when this pass is invoked directly from 'fir-opt' tool.
70   // In that case, 'inputFilename' may be empty. Location embedded in the
71   // module will be used to get file name and its directory.
72   if (inputFilename.empty()) {
73     if (auto fileLoc = mlir::dyn_cast<mlir::FileLineColLoc>(module.getLoc())) {
74       fileName = llvm::sys::path::filename(fileLoc.getFilename().getValue());
75       filePath = llvm::sys::path::parent_path(fileLoc.getFilename().getValue());
76     } else
77       fileName = "-";
78   } else {
79     fileName = inputFilename;
80     llvm::SmallString<256> cwd;
81     if (!llvm::sys::fs::current_path(cwd))
82       filePath = cwd.str();
83   }
84 
85   mlir::LLVM::DIFileAttr fileAttr =
86       mlir::LLVM::DIFileAttr::get(context, fileName, filePath);
87   mlir::StringAttr producer =
88       mlir::StringAttr::get(context, Fortran::common::getFlangFullVersion());
89   mlir::LLVM::DICompileUnitAttr cuAttr = mlir::LLVM::DICompileUnitAttr::get(
90       mlir::DistinctAttr::create(mlir::UnitAttr::get(context)),
91       llvm::dwarf::getLanguage("DW_LANG_Fortran95"), fileAttr, producer,
92       isOptimized, debugLevel);
93 
94   module.walk([&](mlir::func::FuncOp funcOp) {
95     mlir::Location l = funcOp->getLoc();
96     // If fused location has already been created then nothing to do
97     // Otherwise, create a fused location.
98     if (mlir::dyn_cast<mlir::FusedLoc>(l))
99       return;
100 
101     unsigned int CC = (funcOp.getName() == fir::NameUniquer::doProgramEntry())
102                           ? llvm::dwarf::getCallingConvention("DW_CC_program")
103                           : llvm::dwarf::getCallingConvention("DW_CC_normal");
104 
105     if (auto funcLoc = mlir::dyn_cast<mlir::FileLineColLoc>(l)) {
106       fileName = llvm::sys::path::filename(funcLoc.getFilename().getValue());
107       filePath = llvm::sys::path::parent_path(funcLoc.getFilename().getValue());
108     }
109 
110     mlir::StringAttr fullName =
111         mlir::StringAttr::get(context, funcOp.getName());
112     auto result = fir::NameUniquer::deconstruct(funcOp.getName());
113     mlir::StringAttr funcName =
114         mlir::StringAttr::get(context, result.second.name);
115 
116     llvm::SmallVector<mlir::LLVM::DITypeAttr> types;
117     fir::DebugTypeGenerator typeGen(module);
118     for (auto resTy : funcOp.getResultTypes()) {
119       auto tyAttr =
120           typeGen.convertType(resTy, fileAttr, cuAttr, funcOp.getLoc());
121       types.push_back(tyAttr);
122     }
123     for (auto inTy : funcOp.getArgumentTypes()) {
124       auto tyAttr = typeGen.convertType(fir::unwrapRefType(inTy), fileAttr,
125                                         cuAttr, funcOp.getLoc());
126       types.push_back(tyAttr);
127     }
128 
129     mlir::LLVM::DISubroutineTypeAttr subTypeAttr =
130         mlir::LLVM::DISubroutineTypeAttr::get(context, CC, types);
131     mlir::LLVM::DIFileAttr funcFileAttr =
132         mlir::LLVM::DIFileAttr::get(context, fileName, filePath);
133 
134     // Only definitions need a distinct identifier and a compilation unit.
135     mlir::DistinctAttr id;
136     mlir::LLVM::DICompileUnitAttr compilationUnit;
137     mlir::LLVM::DISubprogramFlags subprogramFlags =
138         mlir::LLVM::DISubprogramFlags{};
139     if (isOptimized)
140       subprogramFlags = mlir::LLVM::DISubprogramFlags::Optimized;
141     if (!funcOp.isExternal()) {
142       id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
143       compilationUnit = cuAttr;
144       subprogramFlags =
145           subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;
146     }
147     unsigned line = 1;
148     if (auto funcLoc = mlir::dyn_cast<mlir::FileLineColLoc>(l))
149       line = funcLoc.getLine();
150 
151     auto spAttr = mlir::LLVM::DISubprogramAttr::get(
152         context, id, compilationUnit, fileAttr, funcName, fullName,
153         funcFileAttr, line, line, subprogramFlags, subTypeAttr);
154     funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr));
155   });
156 }
157 
158 std::unique_ptr<mlir::Pass>
159 fir::createAddDebugInfoPass(fir::AddDebugInfoOptions options) {
160   return std::make_unique<AddDebugInfoPass>(options);
161 }
162