[mlir] Translate nested debug information (#140915)
This backports changes from Triton with the exception that for fused locations, use the first one with file info rather than just first. --------- Co-authored-by: Sergei Lebedev <slebedev@google.com> Co-authored-by: Keren Zhou <kerenzhou@openai.com>
This commit is contained in:
@@ -31,6 +31,14 @@ static FileLineColLoc extractFileLoc(Location loc) {
|
||||
return extractFileLoc(nameLoc.getChildLoc());
|
||||
if (auto opaqueLoc = dyn_cast<OpaqueLoc>(loc))
|
||||
return extractFileLoc(opaqueLoc.getFallbackLocation());
|
||||
if (auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
|
||||
for (auto loc : fusedLoc.getLocations()) {
|
||||
if (auto fileLoc = extractFileLoc(loc))
|
||||
return fileLoc;
|
||||
}
|
||||
}
|
||||
if (auto callerLoc = dyn_cast<CallSiteLoc>(loc))
|
||||
return extractFileLoc(callerLoc.getCaller());
|
||||
return FileLineColLoc();
|
||||
}
|
||||
|
||||
@@ -41,47 +49,84 @@ static void addScopeToFunction(LLVM::LLVMFuncOp llvmFunc,
|
||||
LLVM::DICompileUnitAttr compileUnitAttr) {
|
||||
|
||||
Location loc = llvmFunc.getLoc();
|
||||
if (loc->findInstanceOf<mlir::FusedLocWith<LLVM::DISubprogramAttr>>())
|
||||
if (loc->findInstanceOf<FusedLocWith<LLVM::DISubprogramAttr>>())
|
||||
return;
|
||||
|
||||
MLIRContext *context = llvmFunc->getContext();
|
||||
|
||||
// Filename, line and colmun to associate to the function.
|
||||
// Filename and line associate to the function.
|
||||
LLVM::DIFileAttr fileAttr;
|
||||
int64_t line = 1, col = 1;
|
||||
FileLineColLoc fileLoc = extractFileLoc(loc);
|
||||
if (!fileLoc && compileUnitAttr) {
|
||||
fileAttr = compileUnitAttr.getFile();
|
||||
} else if (!fileLoc) {
|
||||
fileAttr = LLVM::DIFileAttr::get(context, "<unknown>", "");
|
||||
} else {
|
||||
int64_t line = 1;
|
||||
if (FileLineColLoc fileLoc = extractFileLoc(loc)) {
|
||||
line = fileLoc.getLine();
|
||||
col = fileLoc.getColumn();
|
||||
StringRef inputFilePath = fileLoc.getFilename().getValue();
|
||||
fileAttr =
|
||||
LLVM::DIFileAttr::get(context, llvm::sys::path::filename(inputFilePath),
|
||||
llvm::sys::path::parent_path(inputFilePath));
|
||||
} else {
|
||||
fileAttr = compileUnitAttr
|
||||
? compileUnitAttr.getFile()
|
||||
: LLVM::DIFileAttr::get(context, "<unknown>", "");
|
||||
}
|
||||
auto subroutineTypeAttr =
|
||||
LLVM::DISubroutineTypeAttr::get(context, llvm::dwarf::DW_CC_normal, {});
|
||||
|
||||
// Only definitions need a distinct identifier and a compilation unit.
|
||||
// Figure out debug information (`subprogramFlags` and `compileUnitAttr`) to
|
||||
// attach to the function definition / declaration. External functions are
|
||||
// declarations only and are defined in a different compile unit, so mark
|
||||
// them appropriately in `subprogramFlags` and set an empty `compileUnitAttr`.
|
||||
DistinctAttr id;
|
||||
auto subprogramFlags = LLVM::DISubprogramFlags::Optimized;
|
||||
if (!llvmFunc.isExternal()) {
|
||||
id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
|
||||
id = DistinctAttr::create(UnitAttr::get(context));
|
||||
subprogramFlags = subprogramFlags | LLVM::DISubprogramFlags::Definition;
|
||||
} else {
|
||||
compileUnitAttr = {};
|
||||
}
|
||||
auto funcName = StringAttr::get(context, llvmFunc.getName());
|
||||
auto funcNameAttr = llvmFunc.getNameAttr();
|
||||
auto subprogramAttr = LLVM::DISubprogramAttr::get(
|
||||
context, id, compileUnitAttr, fileAttr, funcName, funcName, fileAttr,
|
||||
/*line=*/line, /*scopeline=*/col, subprogramFlags, subroutineTypeAttr,
|
||||
context, id, compileUnitAttr, fileAttr, funcNameAttr, funcNameAttr,
|
||||
fileAttr,
|
||||
/*line=*/line, /*scopeLine=*/line, subprogramFlags, subroutineTypeAttr,
|
||||
/*retainedNodes=*/{}, /*annotations=*/{});
|
||||
llvmFunc->setLoc(FusedLoc::get(context, {loc}, subprogramAttr));
|
||||
}
|
||||
|
||||
// Get a nested loc for inlined functions.
|
||||
static Location getNestedLoc(Operation *op, LLVM::DIScopeAttr scopeAttr,
|
||||
Location calleeLoc) {
|
||||
auto calleeFileName = extractFileLoc(calleeLoc).getFilename();
|
||||
auto *context = op->getContext();
|
||||
LLVM::DIFileAttr calleeFileAttr =
|
||||
LLVM::DIFileAttr::get(context, llvm::sys::path::filename(calleeFileName),
|
||||
llvm::sys::path::parent_path(calleeFileName));
|
||||
auto lexicalBlockFileAttr = LLVM::DILexicalBlockFileAttr::get(
|
||||
context, scopeAttr, calleeFileAttr, /*discriminator=*/0);
|
||||
Location loc = calleeLoc;
|
||||
// Recurse if the callee location is again a call site.
|
||||
if (auto callSiteLoc = dyn_cast<CallSiteLoc>(calleeLoc)) {
|
||||
auto nestedLoc = callSiteLoc.getCallee();
|
||||
loc = getNestedLoc(op, lexicalBlockFileAttr, nestedLoc);
|
||||
}
|
||||
return FusedLoc::get(context, {loc}, lexicalBlockFileAttr);
|
||||
}
|
||||
|
||||
static void setLexicalBlockFileAttr(Operation *op) {
|
||||
if (auto callSiteLoc = dyn_cast<CallSiteLoc>(op->getLoc())) {
|
||||
auto callerLoc = callSiteLoc.getCaller();
|
||||
auto calleeLoc = callSiteLoc.getCallee();
|
||||
LLVM::DIScopeAttr scopeAttr;
|
||||
// We assemble the full inline stack so the parent of this loc must be a
|
||||
// function
|
||||
auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>();
|
||||
if (auto funcOpLoc = llvm::dyn_cast_if_present<FusedLoc>(funcOp.getLoc())) {
|
||||
scopeAttr = cast<LLVM::DISubprogramAttr>(funcOpLoc.getMetadata());
|
||||
op->setLoc(
|
||||
CallSiteLoc::get(getNestedLoc(op, scopeAttr, calleeLoc), callerLoc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// Add a debug info scope to LLVMFuncOp that are missing it.
|
||||
struct DIScopeForLLVMFuncOpPass
|
||||
@@ -99,15 +144,12 @@ struct DIScopeForLLVMFuncOpPass
|
||||
return signalPassFailure();
|
||||
}
|
||||
|
||||
// To find a DICompileUnitAttr attached to a parent (the module for
|
||||
// example), otherwise create a default one.
|
||||
// Find a DICompileUnitAttr attached to the module, otherwise create a
|
||||
// default one.
|
||||
// Find a DICompileUnitAttr attached to a parent (the module for example),
|
||||
// otherwise create a default one.
|
||||
LLVM::DICompileUnitAttr compileUnitAttr;
|
||||
auto fusedCompileUnitAttr =
|
||||
module->getLoc()
|
||||
->findInstanceOf<mlir::FusedLocWith<LLVM::DICompileUnitAttr>>();
|
||||
if (fusedCompileUnitAttr) {
|
||||
if (auto fusedCompileUnitAttr =
|
||||
module->getLoc()
|
||||
->findInstanceOf<FusedLocWith<LLVM::DICompileUnitAttr>>()) {
|
||||
compileUnitAttr = fusedCompileUnitAttr.getMetadata();
|
||||
} else {
|
||||
LLVM::DIFileAttr fileAttr;
|
||||
@@ -126,9 +168,14 @@ struct DIScopeForLLVMFuncOpPass
|
||||
/*isOptimized=*/true, emissionKind);
|
||||
}
|
||||
|
||||
// Create subprograms for each function with the same distinct compile unit.
|
||||
module.walk([&](LLVM::LLVMFuncOp func) {
|
||||
addScopeToFunction(func, compileUnitAttr);
|
||||
module.walk<WalkOrder::PreOrder>([&](Operation *op) -> void {
|
||||
if (auto funcOp = dyn_cast<LLVM::LLVMFuncOp>(op)) {
|
||||
// Create subprograms for each function with the same distinct compile
|
||||
// unit.
|
||||
addScopeToFunction(funcOp, compileUnitAttr);
|
||||
} else {
|
||||
setLexicalBlockFileAttr(op);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,7 +57,7 @@ module {
|
||||
// CHECK-DAG: #[[DI_FILE_FUNC:.+]] = #llvm.di_file<"file.mlir" in "">
|
||||
// CHECK-DAG: #loc[[FUNCFILELOC:[0-9]+]] = loc("file.mlir":9:8)
|
||||
// CHECK-DAG: #di_compile_unit = #llvm.di_compile_unit<id = distinct[{{.*}}]<>, sourceLanguage = DW_LANG_C, file = #[[DI_FILE_MODULE]], producer = "MLIR", isOptimized = true, emissionKind = LineTablesOnly>
|
||||
// CHECK-DAG: #di_subprogram = #llvm.di_subprogram<id = distinct[{{.*}}]<>, compileUnit = #di_compile_unit, scope = #[[DI_FILE_FUNC]], name = "propagate_compile_unit", linkageName = "propagate_compile_unit", file = #[[DI_FILE_FUNC]], line = 9, scopeLine = 8, subprogramFlags = "Definition|Optimized", type = #di_subroutine_type>
|
||||
// CHECK-DAG: #di_subprogram = #llvm.di_subprogram<id = distinct[{{.*}}]<>, compileUnit = #di_compile_unit, scope = #[[DI_FILE_FUNC]], name = "propagate_compile_unit", linkageName = "propagate_compile_unit", file = #[[DI_FILE_FUNC]], line = 9, scopeLine = 9, subprogramFlags = "Definition|Optimized", type = #di_subroutine_type>
|
||||
// CHECK-DAG: #loc[[MODULELOC]] = loc(fused<#di_compile_unit>[#loc])
|
||||
// CHECK-DAG: #loc[[FUNCLOC]] = loc(fused<#di_subprogram>[#loc[[FUNCFILELOC]]
|
||||
module {
|
||||
@@ -83,3 +83,61 @@ module @multiple_funcs {
|
||||
llvm.return loc(unknown)
|
||||
} loc(unknown)
|
||||
} loc(unknown)
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-LABEL: llvm.func @func_inlined()
|
||||
// CHECK: #di_file = #llvm.di_file<"base.py" in "testing">
|
||||
// CHECK: #di_file1 = #llvm.di_file<"gpu_test.py" in "">
|
||||
// CHECK: #di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
|
||||
// CHECK: #di_compile_unit = #llvm.di_compile_unit<id = distinct[0]<>, sourceLanguage = DW_LANG_C, file = #di_file, producer = "MLIR", isOptimized = true, emissionKind = LineTablesOnly>
|
||||
// CHECK: #loc3 = loc(callsite(#loc1 at #loc2))
|
||||
// CHECK: #di_subprogram = #llvm.di_subprogram<id = distinct[1]<>, compileUnit = #di_compile_unit, scope = #di_file1, name = "func_inlined", linkageName = "func_inlined", file = #di_file1, line = 1150, scopeLine = 1150, subprogramFlags = "Definition|Optimized", type = #di_subroutine_type>
|
||||
// CHECK: #di_lexical_block_file = #llvm.di_lexical_block_file<scope = #di_subprogram, file = #di_file, discriminator = 0>
|
||||
|
||||
|
||||
#loc = loc("gpu_test.py":1150:34 to :43)
|
||||
#loc1 = loc("testing/parameterized.py":321:17 to :53)
|
||||
#loc2 = loc("testing/base.py":2904:19 to :56)
|
||||
#loc_1 = loc(callsite(#loc at #loc1))
|
||||
#loc21 = loc(callsite(#loc2 at #loc_1))
|
||||
|
||||
module {
|
||||
llvm.func @func_inlined() {
|
||||
llvm.return loc(#loc21)
|
||||
} loc(#loc)
|
||||
} loc(#loc2)
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-LABEL: llvm.func @func_name_with_child()
|
||||
// CHECK: #di_file = #llvm.di_file<"file" in "/tmp">
|
||||
// CHECK: #di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
|
||||
// CHECK: #di_subprogram = #llvm.di_subprogram<scope = #di_file, name = "func_name_with_child", linkageName = "func_name_with_child", file = #di_file, line = 100, scopeLine = 100, subprogramFlags = Optimized, type = #di_subroutine_type>
|
||||
|
||||
module {
|
||||
llvm.func @func_name_with_child() loc("foo"("/tmp/file":100))
|
||||
} loc(unknown)
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-LABEL: llvm.func @func_fusion()
|
||||
// CHECK: #di_file = #llvm.di_file<"file" in "/tmp">
|
||||
// CHECK: #di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
|
||||
// CHECK: #di_subprogram = #llvm.di_subprogram<scope = #di_file, name = "func_fusion", linkageName = "func_fusion", file = #di_file, line = 20, scopeLine = 20, subprogramFlags = Optimized, type = #di_subroutine_type>
|
||||
|
||||
module {
|
||||
llvm.func @func_fusion() loc(fused<"myPass">["foo", "/tmp/file":20])
|
||||
} loc(unknown)
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-LABEL: llvm.func @func_callsiteloc()
|
||||
// CHECK: #di_file = #llvm.di_file<"mysource.cc" in "">
|
||||
// CHECK: #di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
|
||||
// CHECK: #di_subprogram = #llvm.di_subprogram<scope = #di_file, name = "func_callsiteloc", linkageName = "func_callsiteloc", file = #di_file, line = 10, scopeLine = 10, subprogramFlags = Optimized, type = #di_subroutine_type>
|
||||
|
||||
module {
|
||||
llvm.func @func_callsiteloc() loc(callsite("foo" at "mysource.cc":10:8))
|
||||
} loc(unknown)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user