diff --git a/flang/include/flang/Lower/Bridge.h b/flang/include/flang/Lower/Bridge.h --- a/flang/include/flang/Lower/Bridge.h +++ b/flang/include/flang/Lower/Bridge.h @@ -57,10 +57,11 @@ const Fortran::parser::AllCookedSources &allCooked, llvm::StringRef triple, fir::KindMapping &kindMap, const Fortran::lower::LoweringOptions &loweringOptions, - const std::vector &envDefaults) { + const std::vector &envDefaults, + llvm::StringRef filePath) { return LoweringBridge(ctx, semanticsContext, defaultKinds, intrinsics, targetCharacteristics, allCooked, triple, kindMap, - loweringOptions, envDefaults); + loweringOptions, envDefaults, filePath); } //===--------------------------------------------------------------------===// @@ -129,7 +130,8 @@ const Fortran::parser::AllCookedSources &cooked, llvm::StringRef triple, fir::KindMapping &kindMap, const Fortran::lower::LoweringOptions &loweringOptions, - const std::vector &envDefaults); + const std::vector &envDefaults, + llvm::StringRef filePath); LoweringBridge() = delete; LoweringBridge(const LoweringBridge &) = delete; diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h --- a/flang/include/flang/Optimizer/Transforms/Passes.h +++ b/flang/include/flang/Optimizer/Transforms/Passes.h @@ -55,6 +55,7 @@ std::unique_ptr createPromoteToAffinePass(); std::unique_ptr createMemoryAllocationPass(); std::unique_ptr createSimplifyIntrinsicsPass(); +std::unique_ptr createAddDebugFoundationPass(); std::unique_ptr createMemoryAllocationPass(bool dynOnHeap, std::size_t maxStackSize); diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td --- a/flang/include/flang/Optimizer/Transforms/Passes.td +++ b/flang/include/flang/Optimizer/Transforms/Passes.td @@ -178,6 +178,19 @@ ]; } +// This needs to be a "mlir::ModuleOp" pass, because we are creating debug for +// the module in this pass. +def AddDebugFoundation : Pass<"add-debug-foundation", "mlir::ModuleOp"> { + let summary = "Add the foundation for debug info"; + let description = [{ + Add the foundation for emitting debug info that can be understood by llvm. + }]; + let constructor = "::fir::createAddDebugFoundationPass()"; + let dependentDialects = [ + "fir::FIROpsDialect", "mlir::func::FuncDialect", "mlir::LLVM::LLVMDialect" + ]; +} + // This needs to be a "mlir::ModuleOp" pass, because it inserts simplified // functions into the module, which is invalid if a finer grain mlir::Operation // is used as the pass specification says to not touch things outside hte scope diff --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc --- a/flang/include/flang/Tools/CLOptions.inc +++ b/flang/include/flang/Tools/CLOptions.inc @@ -65,6 +65,7 @@ #if !defined(FLANG_EXCLUDE_CODEGEN) DisableOption(CodeGenRewrite, "codegen-rewrite", "rewrite FIR for codegen"); DisableOption(TargetRewrite, "target-rewrite", "rewrite FIR for target"); +DisableOption(DebugFoundation, "debug-foundation", "Add debug foundation"); DisableOption(FirToLlvmIr, "fir-to-llvmir", "FIR to LLVM-IR dialect"); DisableOption(LlvmIrToLlvm, "llvm", "conversion to LLVM"); DisableOption(BoxedProcedureRewrite, "boxed-procedure-rewrite", @@ -127,6 +128,11 @@ }); } +inline void addDebugFoundationPass(mlir::PassManager &pm) { + addPassConditionally(pm, disableDebugFoundation, + [&]() { return fir::createAddDebugFoundationPass(); }); +} + inline void addFIRToLLVMPass(mlir::PassManager &pm) { fir::FIRToLLVMPassOptions options; options.ignoreMissingTypeDescriptors = ignoreMissingTypeDescriptors; diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -171,7 +171,8 @@ ci.getInvocation().getSemanticsContext().targetCharacteristics(), ci.getParsing().allCooked(), ci.getInvocation().getTargetOpts().triple, kindMap, ci.getInvocation().getLoweringOpts(), - ci.getInvocation().getFrontendOpts().envDefaults); + ci.getInvocation().getFrontendOpts().envDefaults, + getCurrentFileOrBufferName()); // Fetch module from lb, so we can set mlirModule = std::make_unique(lb.getModule()); diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -3792,7 +3792,8 @@ const Fortran::parser::AllCookedSources &cooked, llvm::StringRef triple, fir::KindMapping &kindMap, const Fortran::lower::LoweringOptions &loweringOptions, - const std::vector &envDefaults) + const std::vector &envDefaults, + llvm::StringRef filePath) : semanticsContext{semanticsContext}, defaultKinds{defaultKinds}, intrinsics{intrinsics}, targetCharacteristics{targetCharacteristics}, cooked{&cooked}, context{context}, kindMap{kindMap}, @@ -3822,7 +3823,8 @@ // Create the module and attach the attributes. module = std::make_unique( - mlir::ModuleOp::create(mlir::UnknownLoc::get(&context))); + mlir::ModuleOp::create(mlir::FileLineColLoc::get( + &getMLIRContext(), filePath, /*line=*/0, /*col=*/0))); assert(module.get() && "module was not created"); fir::setTargetTriple(*module.get(), triple); fir::setKindMapping(*module.get(), kindMap); diff --git a/flang/lib/Optimizer/Transforms/AddDebugFoundation.cpp b/flang/lib/Optimizer/Transforms/AddDebugFoundation.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Optimizer/Transforms/AddDebugFoundation.cpp @@ -0,0 +1,89 @@ +//===- AddDebugFoundation.cpp -- add basic debug linetable info -----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +/// \file +/// This pass populates some debug information for the module and functions. +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Builder/FIRBuilder.h" +#include "flang/Optimizer/Builder/Todo.h" +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "flang/Optimizer/Dialect/FIROps.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "flang/Optimizer/Support/FIRContext.h" +#include "flang/Optimizer/Transforms/Passes.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/IR/Matchers.h" +#include "mlir/IR/TypeUtilities.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Transforms/DialectConversion.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "mlir/Transforms/RegionUtils.h" +#include "llvm/ADT/Optional.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +namespace fir { +#define GEN_PASS_DEF_ADDDEBUGFOUNDATION +#define GEN_PASS_DECL_ADDDEBUGFOUNDATION +#include "flang/Optimizer/Transforms/Passes.h.inc" +} // namespace fir + +#define DEBUG_TYPE "flang-add-debug-foundation" + +namespace { + +class AddDebugFoundationPass + : public fir::impl::AddDebugFoundationBase { +public: + void runOnOperation() override; +}; + +} // namespace + +void AddDebugFoundationPass::runOnOperation() { + mlir::ModuleOp module = getOperation(); + mlir::MLIRContext *context = &getContext(); + mlir::OpBuilder builder(context); + std::string inputFilePath("-"); + if (auto fileLoc = module.getLoc().dyn_cast()) + inputFilePath = fileLoc.getFilename().getValue(); + + mlir::LLVM::DIFileAttr fileAttr = mlir::LLVM::DIFileAttr::get( + context, llvm::sys::path::filename(inputFilePath), + llvm::sys::path::parent_path(inputFilePath)); + mlir::StringAttr producer = mlir::StringAttr::get(context, "Flang"); + mlir::LLVM::DICompileUnitAttr cuAttr = mlir::LLVM::DICompileUnitAttr::get( + context, llvm::dwarf::getLanguage("DW_LANG_Fortran95"), fileAttr, + producer, /*isOptimized=*/false, + mlir::LLVM::DIEmissionKind::LineTablesOnly); + module.walk([&](mlir::func::FuncOp funcOp) { + mlir::StringAttr funcName = + mlir::StringAttr::get(context, funcOp.getName()); + mlir::LLVM::DIBasicTypeAttr bT = mlir::LLVM::DIBasicTypeAttr::get( + context, llvm::dwarf::DW_TAG_base_type, "void", /*sizeInBits=*/0, + /*encoding=*/1); + mlir::LLVM::DISubroutineTypeAttr subTypeAttr = + mlir::LLVM::DISubroutineTypeAttr::get( + context, llvm::dwarf::getCallingConvention("DW_CC_normal"), bT, + {bT}); + mlir::LLVM::DISubprogramAttr spAttr = mlir::LLVM::DISubprogramAttr::get( + context, cuAttr, fileAttr, funcName, funcName, fileAttr, /*line=*/1, + /*scopeline=*/1, mlir::LLVM::DISubprogramFlags::Definition, + subTypeAttr); + funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr)); + }); +} + +std::unique_ptr fir::createAddDebugFoundationPass() { + return std::make_unique(); +} diff --git a/flang/lib/Optimizer/Transforms/CMakeLists.txt b/flang/lib/Optimizer/Transforms/CMakeLists.txt --- a/flang/lib/Optimizer/Transforms/CMakeLists.txt +++ b/flang/lib/Optimizer/Transforms/CMakeLists.txt @@ -12,6 +12,7 @@ SimplifyRegionLite.cpp AlgebraicSimplification.cpp SimplifyIntrinsics.cpp + AddDebugFoundation.cpp DEPENDS FIRBuilder diff --git a/flang/test/Lower/module-debug-file-loc.f90 b/flang/test/Lower/module-debug-file-loc.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/module-debug-file-loc.f90 @@ -0,0 +1,12 @@ +! Test that the module has the location information + +! RUN: %flang_fc1 -mmlir --mlir-print-debuginfo -emit-fir -o - %s | FileCheck %s + +subroutine sb1() +end subroutine + +! CHECK: module attributes +! CHECK: func.func @_QPsb1() { +! CHECK: } +! CHECK: } loc(#[[MODULE_LOC:.*]]) +! CHECK: #[[MODULE_LOC]] = loc("{{.*}}module-debug-file-loc.f90":0:0) diff --git a/flang/test/Transforms/debug-line-table.fir b/flang/test/Transforms/debug-line-table.fir new file mode 100644 --- /dev/null +++ b/flang/test/Transforms/debug-line-table.fir @@ -0,0 +1,24 @@ + +// RUN: fir-opt --add-debug-foundation --mlir-print-debuginfo %s | FileCheck %s + +module attributes { fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", llvm.target_triple = "aarch64-unknown-linux-gnu"} { + func.func @_QPsb() { + return loc(#loc_sb) + } loc(#loc_sb) +} loc(#loc_module) +#loc_module = loc("./simple.f90":1:1) +#loc_sb = loc("./simple.f90":2:1) + +// CHECK: module attributes +// CHECK: func.func @[[SB_NAME:.*]]() { +// CHECK: return loc(#[[SB_LOC:.*]]) +// CHECK: } loc(#[[FUSED_SB_LOC:.*]]) +// CHECK: } loc(#[[MODULE_LOC:.*]]) +// CHECK: #di_basic_type = #llvm.di_basic_type +// CHECK: #di_file = #llvm.di_file<"[[FILE_NAME:.*]]" in "[[DIR_NAME:.*]]"> +// CHECK: #[[MODULE_LOC]] = loc("[[DIR_NAME]]/[[FILE_NAME]]":1:1) +// CHECK: #[[SB_LOC]] = loc("./simple.f90":2:1) +// CHECK: #di_compile_unit = #llvm.di_compile_unit +// CHECK: #di_subroutine_type = #llvm.di_subroutine_type +// CHECK: #di_subprogram = #llvm.di_subprogram +// CHECK: #[[FUSED_SB_LOC]] = loc(fused<#di_subprogram>[#[[SB_LOC]]]) diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp --- a/flang/tools/bbc/bbc.cpp +++ b/flang/tools/bbc/bbc.cpp @@ -235,7 +235,7 @@ auto burnside = Fortran::lower::LoweringBridge::create( ctx, semanticsContext, defKinds, semanticsContext.intrinsics(), semanticsContext.targetCharacteristics(), parsing.allCooked(), "", - kindMap, loweringOptions, {}); + kindMap, loweringOptions, {}, inputFilename); burnside.lower(parseTree, semanticsContext); mlir::ModuleOp mlirModule = burnside.getModule(); std::error_code ec;