Skip to content

Commit 0a88e96

Browse files
authored
[MLIR][LLVM] Extend DIScopeForLLVMFuncOp to handle cross-file operatio… (#167844)
The current `DIScopeForLLVMFuncOp` pass handles debug information for inlined code by processing `CallSiteLoc` attributes. However, some compilation scenarios compose code from multiple source files directly into a single function without generating `CallSiteLoc`. **Scenario:** ```python # a.py def kernel_a(tensor): print("a: {}", tensor) # a.py:3 jit_func_b(tensor) # Calls b.py code # b.py def func_b(tensor): print("b: {}", tensor) # b.py:7 ``` The scenario executes Python at compile-time and directly inserts operations from `b.py` into the kernel function, resulting in MLIR like: ```mlir @kernel_a(...) { print("a: {}", %arg0) loc(#loc_a) // a.py:3 print("b: {}", %arg0) loc(#loc_b) // b.py:7 <- FileLineColLoc, not CallSiteLoc } loc(#loc_kernel) // a.py:1 #loc1 = loc("a.py":3:.) #loc2 = loc("b.py":7:.) #loc_a = loc("print"(#loc1)) #loc_b = loc("print"(#loc2)) ``` ```llvm !6 = !DIFile(filename: "a.py", directory: "...") !9 = distinct !DISubprogram(name: "...", linkageName: "...", scope: !6, file: !6, line: 13, ...) !10 = !DILocation(line: 7, column: ., scope: !9) // Points to kernel's DISubprogram, not correct ```
1 parent 53dfdf7 commit 0a88e96

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,12 @@ static Location getNestedLoc(Operation *op, LLVM::DIScopeAttr scopeAttr,
109109
return FusedLoc::get(context, {loc}, lexicalBlockFileAttr);
110110
}
111111

112+
/// Adds DILexicalBlockFileAttr for operations with CallSiteLoc and operations
113+
/// from different files than their containing function.
112114
static void setLexicalBlockFileAttr(Operation *op) {
113-
if (auto callSiteLoc = dyn_cast<CallSiteLoc>(op->getLoc())) {
115+
Location opLoc = op->getLoc();
116+
117+
if (auto callSiteLoc = dyn_cast<CallSiteLoc>(opLoc)) {
114118
auto callerLoc = callSiteLoc.getCaller();
115119
auto calleeLoc = callSiteLoc.getCallee();
116120
LLVM::DIScopeAttr scopeAttr;
@@ -122,6 +126,45 @@ static void setLexicalBlockFileAttr(Operation *op) {
122126
op->setLoc(
123127
CallSiteLoc::get(getNestedLoc(op, scopeAttr, calleeLoc), callerLoc));
124128
}
129+
130+
return;
131+
}
132+
133+
auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>();
134+
if (!funcOp)
135+
return;
136+
137+
FileLineColLoc opFileLoc = extractFileLoc(opLoc);
138+
if (!opFileLoc)
139+
return;
140+
141+
FileLineColLoc funcFileLoc = extractFileLoc(funcOp.getLoc());
142+
if (!funcFileLoc)
143+
return;
144+
145+
StringRef opFile = opFileLoc.getFilename().getValue();
146+
StringRef funcFile = funcFileLoc.getFilename().getValue();
147+
148+
// Handle cross-file operations: add DILexicalBlockFileAttr when the
149+
// operation's source file differs from its containing function.
150+
if (opFile != funcFile) {
151+
auto funcOpLoc = llvm::dyn_cast_if_present<FusedLoc>(funcOp.getLoc());
152+
if (!funcOpLoc)
153+
return;
154+
auto scopeAttr = dyn_cast<LLVM::DISubprogramAttr>(funcOpLoc.getMetadata());
155+
if (!scopeAttr)
156+
return;
157+
158+
auto *context = op->getContext();
159+
LLVM::DIFileAttr opFileAttr =
160+
LLVM::DIFileAttr::get(context, llvm::sys::path::filename(opFile),
161+
llvm::sys::path::parent_path(opFile));
162+
163+
LLVM::DILexicalBlockFileAttr lexicalBlockFileAttr =
164+
LLVM::DILexicalBlockFileAttr::get(context, scopeAttr, opFileAttr, 0);
165+
166+
Location newLoc = FusedLoc::get(context, {opLoc}, lexicalBlockFileAttr);
167+
op->setLoc(newLoc);
125168
}
126169
}
127170

mlir/test/Dialect/LLVMIR/add-debuginfo-func-scope.mlir

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,22 @@ module {
141141
llvm.func @func_callsiteloc() loc(callsite("foo" at "mysource.cc":10:8))
142142
} loc(unknown)
143143

144+
// -----
145+
146+
// CHECK-LABEL: llvm.func @func_cross_file_op()
147+
// CHECK: #di_file = #llvm.di_file<"<unknown>" in "">
148+
// CHECK: #di_file1 = #llvm.di_file<"caller.py" in "">
149+
// CHECK: #di_file2 = #llvm.di_file<"callee.py" in "">
150+
// CHECK: #di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
151+
// CHECK: #di_subprogram = #llvm.di_subprogram<id = distinct[1]<>, compileUnit = #di_compile_unit, scope = #di_file1, name = "func_cross_file_op", linkageName = "func_cross_file_op", file = #di_file1, line = 5, scopeLine = 5, subprogramFlags = "Definition|Optimized", type = #di_subroutine_type>
152+
// CHECK: #di_lexical_block_file = #llvm.di_lexical_block_file<scope = #di_subprogram, file = #di_file2, discriminator = 0>
153+
154+
#loc = loc("caller.py":5:1)
155+
#loc1 = loc("callee.py":10:5)
156+
157+
module {
158+
llvm.func @func_cross_file_op() {
159+
llvm.return loc(#loc1)
160+
} loc(#loc)
161+
} loc(unknown)
162+

0 commit comments

Comments
 (0)