Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 73 additions & 26 deletions mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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);
}
});
}
};
Expand Down
27 changes: 26 additions & 1 deletion mlir/test/Dialect/LLVMIR/add-debuginfo-func-scope.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -83,3 +83,28 @@ 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)