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