diff --git a/mlir/examples/toy/Ch6/toyc.cpp b/mlir/examples/toy/Ch6/toyc.cpp --- a/mlir/examples/toy/Ch6/toyc.cpp +++ b/mlir/examples/toy/Ch6/toyc.cpp @@ -16,6 +16,7 @@ #include "toy/Passes.h" #include "mlir/Dialect/Affine/Passes.h" +#include "mlir/Dialect/LLVMIR/Transforms/Passes.h" #include "mlir/ExecutionEngine/ExecutionEngine.h" #include "mlir/ExecutionEngine/OptUtils.h" #include "mlir/IR/AsmState.h" @@ -171,6 +172,8 @@ if (isLoweringToLLVM) { // Finish lowering the toy IR to the LLVM dialect. pm.addPass(mlir::toy::createLowerToLLVMPass()); + pm.addNestedPass( + mlir::LLVM::createDIScopeForLLVMFuncOpPass()); } if (mlir::failed(pm.run(*module))) diff --git a/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.h b/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.h --- a/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.h +++ b/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.h @@ -18,6 +18,9 @@ namespace LLVM { +/// Create a pass to add DIScope to LLVMFuncOp that are missing it. +std::unique_ptr createDIScopeForLLVMFuncOpPass(); + /// Generate the code for registering conversion passes. #define GEN_PASS_REGISTRATION #include "mlir/Dialect/LLVMIR/Transforms/Passes.h.inc" diff --git a/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.td b/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.td --- a/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.td @@ -35,4 +35,19 @@ let constructor = "::mlir::NVVM::createOptimizeForTargetPass()"; } +def DIScopeForLLVMFuncOp : Pass<"ensure-debug-info-scope-on-llvm-func", "LLVM::LLVMFuncOp"> { + let summary = "Materialize LLVM debug info subprogram attribute on every LLVMFuncOp"; + let description = [{ + Having a debug info subprogram attribute on a function is required for + emitting line tables from MLIR FileLocCol locations. + + This is not intended to be a proper replacement for frontends to emit + complete debug informations, however it is a convenient way to get line + tables for debugging purposes. This allow to step trough in a debugger + line-by-line or get a backtrace with line numbers. + }]; + + let constructor = "mlir::LLVM::createDIScopeForLLVMFuncOpPass()"; +} + #endif // MLIR_DIALECT_LLVMIR_TRANSFORMS_PASSES diff --git a/mlir/lib/Dialect/LLVMIR/Transforms/CMakeLists.txt b/mlir/lib/Dialect/LLVMIR/Transforms/CMakeLists.txt --- a/mlir/lib/Dialect/LLVMIR/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/LLVMIR/Transforms/CMakeLists.txt @@ -1,4 +1,5 @@ add_mlir_dialect_library(MLIRLLVMIRTransforms + DIScopeForLLVMFuncOp.cpp LegalizeForExport.cpp OptimizeForNVVM.cpp RequestCWrappers.cpp diff --git a/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp b/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp @@ -0,0 +1,102 @@ +//===- DILineTableFromLocations.cpp - -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/LLVMIR/Transforms/Passes.h" + +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Pass/Pass.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Path.h" + +namespace mlir { +namespace LLVM { +#define GEN_PASS_DEF_DISCOPEFORLLVMFUNCOP +#include "mlir/Dialect/LLVMIR/Transforms/Passes.h.inc" +} // namespace LLVM +} // namespace mlir + +using namespace mlir; + +/// Attempt to extract a filename for the given loc. +static FileLineColLoc extractFileLoc(Location loc) { + if (auto fileLoc = loc.dyn_cast()) + return fileLoc; + if (auto nameLoc = loc.dyn_cast()) + return extractFileLoc(nameLoc.getChildLoc()); + if (auto opaqueLoc = loc.dyn_cast()) + return extractFileLoc(opaqueLoc.getFallbackLocation()); + return FileLineColLoc(); +} + +namespace { +/// Canonicalize operations in nested regions. +struct DIScopeForLLVMFuncOp + : public LLVM::impl::DIScopeForLLVMFuncOpBase { + void runOnOperation() override { + LLVM::LLVMFuncOp llvmFunc = getOperation(); + auto loc = llvmFunc.getLoc(); + if (loc->findInstanceOf>()) + return; + + auto *context = &getContext(); + + // To find a DICompileUnitAttr attached to a parent (the module for + // example), otherwise create a default one. + LLVM::DICompileUnitAttr compileUnitAttr; + if (auto module = llvmFunc->getParentOfType()) { + auto fusedCompileUnitAttr = + module->getLoc() + ->findInstanceOf>(); + if (fusedCompileUnitAttr) + compileUnitAttr = fusedCompileUnitAttr.getMetadata(); + } + + // Filename, line and colmun to associate to the function. + LLVM::DIFileAttr fileAttr; + int64_t line = 1, col = 1; + auto fileLoc = extractFileLoc(loc); + if (!fileLoc && compileUnitAttr) { + fileAttr = compileUnitAttr.getFile(); + } else if (!fileLoc) { + fileAttr = LLVM::DIFileAttr::get(context, "", ""); + } else { + 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)); + } + if (!compileUnitAttr) { + compileUnitAttr = LLVM::DICompileUnitAttr::get( + context, llvm::dwarf::DW_LANG_C, fileAttr, + StringAttr::get(context, "MLIR"), /*isOptimized=*/true, + LLVM::DIEmissionKind::LineTablesOnly); + } + auto subroutineTypeAttr = + LLVM::DISubroutineTypeAttr::get(context, llvm::dwarf::DW_CC_normal, {}); + + StringAttr funcNameAttr = llvmFunc.getNameAttr(); + auto subprogramAttr = + LLVM::DISubprogramAttr::get(context, compileUnitAttr, fileAttr, + funcNameAttr, funcNameAttr, fileAttr, + /*line=*/line, + /*scopeline=*/col, + LLVM::DISubprogramFlags::Definition | + LLVM::DISubprogramFlags::Optimized, + subroutineTypeAttr); + llvmFunc->setLoc(FusedLoc::get(context, {loc}, subprogramAttr)); + } +}; + +} // end anonymous namespace + +std::unique_ptr mlir::LLVM::createDIScopeForLLVMFuncOpPass() { + return std::make_unique(); +} \ No newline at end of file