diff --git a/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp b/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp index 6db14ea5bbfd4..4c31f0082a073 100644 --- a/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp +++ b/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp @@ -31,6 +31,14 @@ static FileLineColLoc extractFileLoc(Location loc) { return extractFileLoc(nameLoc.getChildLoc()); if (auto opaqueLoc = dyn_cast(loc)) return extractFileLoc(opaqueLoc.getFallbackLocation()); + if (auto fusedLoc = dyn_cast(loc)) { + for (auto loc : fusedLoc.getLocations()) { + if (auto fileLoc = extractFileLoc(loc)) + return fileLoc; + } + } + if (auto callerLoc = dyn_cast(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>()) + if (loc->findInstanceOf>()) 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, "", ""); - } 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, "", ""); } 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(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(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(); + if (auto funcOpLoc = llvm::dyn_cast_if_present(funcOp.getLoc())) { + scopeAttr = cast(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>(); - if (fusedCompileUnitAttr) { + if (auto fusedCompileUnitAttr = + module->getLoc() + ->findInstanceOf>()) { 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([&](Operation *op) -> void { + if (auto funcOp = dyn_cast(op)) { + // Create subprograms for each function with the same distinct compile + // unit. + addScopeToFunction(funcOp, compileUnitAttr); + } else { + setLexicalBlockFileAttr(op); + } }); } }; diff --git a/mlir/test/Dialect/LLVMIR/add-debuginfo-func-scope.mlir b/mlir/test/Dialect/LLVMIR/add-debuginfo-func-scope.mlir index df7fe2c5c615a..dfbf992f34c10 100644 --- a/mlir/test/Dialect/LLVMIR/add-debuginfo-func-scope.mlir +++ b/mlir/test/Dialect/LLVMIR/add-debuginfo-func-scope.mlir @@ -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, sourceLanguage = DW_LANG_C, file = #[[DI_FILE_MODULE]], producer = "MLIR", isOptimized = true, emissionKind = LineTablesOnly> -// CHECK-DAG: #di_subprogram = #llvm.di_subprogram, 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, 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 +// CHECK: #di_compile_unit = #llvm.di_compile_unit, 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, 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 + + +#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 +// CHECK: #di_subprogram = #llvm.di_subprogram + +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 +// CHECK: #di_subprogram = #llvm.di_subprogram + +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 +// CHECK: #di_subprogram = #llvm.di_subprogram + +module { + llvm.func @func_callsiteloc() loc(callsite("foo" at "mysource.cc":10:8)) +} loc(unknown) +