1//===- DILineTableFromLocations.cpp - -------------------------------------===//
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#include "mlir/Dialect/LLVMIR/Transforms/Passes.h"
10
11#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
12#include "llvm/BinaryFormat/Dwarf.h"
13#include "llvm/Support/Path.h"
14
15namespace mlir {
16namespace LLVM {
17#define GEN_PASS_DEF_DISCOPEFORLLVMFUNCOPPASS
18#include "mlir/Dialect/LLVMIR/Transforms/Passes.h.inc"
19} // namespace LLVM
20} // namespace mlir
21
22using namespace mlir;
23
24/// Attempt to extract a filename for the given loc.
25static FileLineColLoc extractFileLoc(Location loc) {
26 if (auto fileLoc = dyn_cast<FileLineColLoc>(Val&: loc))
27 return fileLoc;
28 if (auto nameLoc = dyn_cast<NameLoc>(Val&: loc))
29 return extractFileLoc(loc: nameLoc.getChildLoc());
30 if (auto opaqueLoc = dyn_cast<OpaqueLoc>(Val&: loc))
31 return extractFileLoc(loc: opaqueLoc.getFallbackLocation());
32 if (auto fusedLoc = dyn_cast<FusedLoc>(Val&: loc)) {
33 for (auto loc : fusedLoc.getLocations()) {
34 if (auto fileLoc = extractFileLoc(loc))
35 return fileLoc;
36 }
37 }
38 if (auto callerLoc = dyn_cast<CallSiteLoc>(Val&: loc))
39 return extractFileLoc(loc: callerLoc.getCaller());
40 return FileLineColLoc();
41}
42
43/// Creates a DISubprogramAttr with the provided compile unit and attaches it
44/// to the function. Does nothing when the function already has an attached
45/// subprogram.
46static void addScopeToFunction(LLVM::LLVMFuncOp llvmFunc,
47 LLVM::DICompileUnitAttr compileUnitAttr) {
48
49 Location loc = llvmFunc.getLoc();
50 if (loc->findInstanceOf<FusedLocWith<LLVM::DISubprogramAttr>>())
51 return;
52
53 MLIRContext *context = llvmFunc->getContext();
54
55 // Filename and line associate to the function.
56 LLVM::DIFileAttr fileAttr;
57 int64_t line = 1;
58 if (FileLineColLoc fileLoc = extractFileLoc(loc)) {
59 line = fileLoc.getLine();
60 StringRef inputFilePath = fileLoc.getFilename().getValue();
61 fileAttr =
62 LLVM::DIFileAttr::get(context, name: llvm::sys::path::filename(path: inputFilePath),
63 directory: llvm::sys::path::parent_path(path: inputFilePath));
64 } else {
65 fileAttr = compileUnitAttr
66 ? compileUnitAttr.getFile()
67 : LLVM::DIFileAttr::get(context, name: "<unknown>", directory: "");
68 }
69 auto subroutineTypeAttr =
70 LLVM::DISubroutineTypeAttr::get(context, callingConvention: llvm::dwarf::DW_CC_normal, types: {});
71
72 // Figure out debug information (`subprogramFlags` and `compileUnitAttr`) to
73 // attach to the function definition / declaration. External functions are
74 // declarations only and are defined in a different compile unit, so mark
75 // them appropriately in `subprogramFlags` and set an empty `compileUnitAttr`.
76 DistinctAttr id;
77 auto subprogramFlags = LLVM::DISubprogramFlags::Optimized;
78 if (!llvmFunc.isExternal()) {
79 id = DistinctAttr::create(referencedAttr: UnitAttr::get(context));
80 subprogramFlags = subprogramFlags | LLVM::DISubprogramFlags::Definition;
81 } else {
82 compileUnitAttr = {};
83 }
84 auto funcNameAttr = llvmFunc.getNameAttr();
85 auto subprogramAttr = LLVM::DISubprogramAttr::get(
86 context, id, compileUnit: compileUnitAttr, scope: fileAttr, name: funcNameAttr, linkageName: funcNameAttr,
87 file: fileAttr,
88 /*line=*/line, /*scopeLine=*/line, subprogramFlags, type: subroutineTypeAttr,
89 /*retainedNodes=*/{}, /*annotations=*/{});
90 llvmFunc->setLoc(FusedLoc::get(context, locations: {loc}, metadata: subprogramAttr));
91}
92
93// Get a nested loc for inlined functions.
94static Location getNestedLoc(Operation *op, LLVM::DIScopeAttr scopeAttr,
95 Location calleeLoc) {
96 auto calleeFileName = extractFileLoc(loc: calleeLoc).getFilename();
97 auto *context = op->getContext();
98 LLVM::DIFileAttr calleeFileAttr =
99 LLVM::DIFileAttr::get(context, name: llvm::sys::path::filename(path: calleeFileName),
100 directory: llvm::sys::path::parent_path(path: calleeFileName));
101 auto lexicalBlockFileAttr = LLVM::DILexicalBlockFileAttr::get(
102 context, scope: scopeAttr, file: calleeFileAttr, /*discriminator=*/0);
103 Location loc = calleeLoc;
104 // Recurse if the callee location is again a call site.
105 if (auto callSiteLoc = dyn_cast<CallSiteLoc>(Val&: calleeLoc)) {
106 auto nestedLoc = callSiteLoc.getCallee();
107 loc = getNestedLoc(op, scopeAttr: lexicalBlockFileAttr, calleeLoc: nestedLoc);
108 }
109 return FusedLoc::get(context, locations: {loc}, metadata: lexicalBlockFileAttr);
110}
111
112static void setLexicalBlockFileAttr(Operation *op) {
113 if (auto callSiteLoc = dyn_cast<CallSiteLoc>(Val: op->getLoc())) {
114 auto callerLoc = callSiteLoc.getCaller();
115 auto calleeLoc = callSiteLoc.getCallee();
116 LLVM::DIScopeAttr scopeAttr;
117 // We assemble the full inline stack so the parent of this loc must be a
118 // function
119 auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>();
120 if (auto funcOpLoc = llvm::dyn_cast_if_present<FusedLoc>(Val: funcOp.getLoc())) {
121 scopeAttr = cast<LLVM::DISubprogramAttr>(Val: funcOpLoc.getMetadata());
122 op->setLoc(
123 CallSiteLoc::get(callee: getNestedLoc(op, scopeAttr, calleeLoc), caller: callerLoc));
124 }
125 }
126}
127
128namespace {
129/// Add a debug info scope to LLVMFuncOp that are missing it.
130struct DIScopeForLLVMFuncOpPass
131 : public LLVM::impl::DIScopeForLLVMFuncOpPassBase<
132 DIScopeForLLVMFuncOpPass> {
133 using Base::Base;
134
135 void runOnOperation() override {
136 ModuleOp module = getOperation();
137 Location loc = module.getLoc();
138
139 MLIRContext *context = &getContext();
140 if (!context->getLoadedDialect<LLVM::LLVMDialect>()) {
141 emitError(loc, message: "LLVM dialect is not loaded.");
142 return signalPassFailure();
143 }
144
145 // Find a DICompileUnitAttr attached to a parent (the module for example),
146 // otherwise create a default one.
147 LLVM::DICompileUnitAttr compileUnitAttr;
148 if (auto fusedCompileUnitAttr =
149 module->getLoc()
150 ->findInstanceOf<FusedLocWith<LLVM::DICompileUnitAttr>>()) {
151 compileUnitAttr = fusedCompileUnitAttr.getMetadata();
152 } else {
153 LLVM::DIFileAttr fileAttr;
154 if (FileLineColLoc fileLoc = extractFileLoc(loc)) {
155 StringRef inputFilePath = fileLoc.getFilename().getValue();
156 fileAttr = LLVM::DIFileAttr::get(
157 context, name: llvm::sys::path::filename(path: inputFilePath),
158 directory: llvm::sys::path::parent_path(path: inputFilePath));
159 } else {
160 fileAttr = LLVM::DIFileAttr::get(context, name: "<unknown>", directory: "");
161 }
162
163 compileUnitAttr = LLVM::DICompileUnitAttr::get(
164 id: DistinctAttr::create(referencedAttr: UnitAttr::get(context)), sourceLanguage: llvm::dwarf::DW_LANG_C,
165 file: fileAttr, producer: StringAttr::get(context, bytes: "MLIR"),
166 /*isOptimized=*/true, emissionKind);
167 }
168
169 module.walk<WalkOrder::PreOrder>(callback: [&](Operation *op) -> void {
170 if (auto funcOp = dyn_cast<LLVM::LLVMFuncOp>(Val: op)) {
171 // Create subprograms for each function with the same distinct compile
172 // unit.
173 addScopeToFunction(llvmFunc: funcOp, compileUnitAttr);
174 } else {
175 setLexicalBlockFileAttr(op);
176 }
177 });
178 }
179};
180
181} // end anonymous namespace
182

source code of mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp