From ba373a41b96081616c7fad9da147105245911687 Mon Sep 17 00:00:00 2001 From: Zichen Lu Date: Thu, 13 Nov 2025 14:37:54 +0800 Subject: [PATCH] [MLIR][LLVM] Extend DIScopeForLLVMFuncOp to handle cross-file operations without CallSiteLoc --- .../Transforms/DIScopeForLLVMFuncOp.cpp | 45 ++++++++++++++++++- .../LLVMIR/add-debuginfo-func-scope.mlir | 19 ++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp b/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp index 67573c4ee6061..12dd22581a979 100644 --- a/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp +++ b/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp @@ -109,8 +109,12 @@ static Location getNestedLoc(Operation *op, LLVM::DIScopeAttr scopeAttr, return FusedLoc::get(context, {loc}, lexicalBlockFileAttr); } +/// Adds DILexicalBlockFileAttr for operations with CallSiteLoc and operations +/// from different files than their containing function. static void setLexicalBlockFileAttr(Operation *op) { - if (auto callSiteLoc = dyn_cast(op->getLoc())) { + Location opLoc = op->getLoc(); + + if (auto callSiteLoc = dyn_cast(opLoc)) { auto callerLoc = callSiteLoc.getCaller(); auto calleeLoc = callSiteLoc.getCallee(); LLVM::DIScopeAttr scopeAttr; @@ -122,6 +126,45 @@ static void setLexicalBlockFileAttr(Operation *op) { op->setLoc( CallSiteLoc::get(getNestedLoc(op, scopeAttr, calleeLoc), callerLoc)); } + + return; + } + + auto funcOp = op->getParentOfType(); + if (!funcOp) + return; + + FileLineColLoc opFileLoc = extractFileLoc(opLoc); + if (!opFileLoc) + return; + + FileLineColLoc funcFileLoc = extractFileLoc(funcOp.getLoc()); + if (!funcFileLoc) + return; + + StringRef opFile = opFileLoc.getFilename().getValue(); + StringRef funcFile = funcFileLoc.getFilename().getValue(); + + // Handle cross-file operations: add DILexicalBlockFileAttr when the + // operation's source file differs from its containing function. + if (opFile != funcFile) { + auto funcOpLoc = llvm::dyn_cast_if_present(funcOp.getLoc()); + if (!funcOpLoc) + return; + auto scopeAttr = dyn_cast(funcOpLoc.getMetadata()); + if (!scopeAttr) + return; + + auto *context = op->getContext(); + LLVM::DIFileAttr opFileAttr = + LLVM::DIFileAttr::get(context, llvm::sys::path::filename(opFile), + llvm::sys::path::parent_path(opFile)); + + LLVM::DILexicalBlockFileAttr lexicalBlockFileAttr = + LLVM::DILexicalBlockFileAttr::get(context, scopeAttr, opFileAttr, 0); + + Location newLoc = FusedLoc::get(context, {opLoc}, lexicalBlockFileAttr); + op->setLoc(newLoc); } } diff --git a/mlir/test/Dialect/LLVMIR/add-debuginfo-func-scope.mlir b/mlir/test/Dialect/LLVMIR/add-debuginfo-func-scope.mlir index dfbf992f34c10..ffeb871d56c6c 100644 --- a/mlir/test/Dialect/LLVMIR/add-debuginfo-func-scope.mlir +++ b/mlir/test/Dialect/LLVMIR/add-debuginfo-func-scope.mlir @@ -141,3 +141,22 @@ module { llvm.func @func_callsiteloc() loc(callsite("foo" at "mysource.cc":10:8)) } loc(unknown) +// ----- + +// CHECK-LABEL: llvm.func @func_cross_file_op() +// CHECK: #di_file = #llvm.di_file<"" in ""> +// CHECK: #di_file1 = #llvm.di_file<"caller.py" in ""> +// CHECK: #di_file2 = #llvm.di_file<"callee.py" in ""> +// CHECK: #di_subroutine_type = #llvm.di_subroutine_type +// CHECK: #di_subprogram = #llvm.di_subprogram, 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> +// CHECK: #di_lexical_block_file = #llvm.di_lexical_block_file + +#loc = loc("caller.py":5:1) +#loc1 = loc("callee.py":10:5) + +module { + llvm.func @func_cross_file_op() { + llvm.return loc(#loc1) + } loc(#loc) +} loc(unknown) +