[flang-commits] [clang] [flang] [flang][draft] Improve debug info generation. (PR #84202)

via flang-commits flang-commits at lists.llvm.org
Wed Mar 6 09:18:18 PST 2024


https://github.com/abidh created https://github.com/llvm/llvm-project/pull/84202

This PR adds initial support for generation of debug information for variables and types. The aim is that it can be the basis of discussion on the design of debug information generation. It is complete enough to allow inspection of variables in GDB but will obviously need more work before it can be considered ready for inclusion.

This PR builds on the existing AddDebugFoundation pass. The existence of this pass and the DeclareOp seems to suggest that debug information was meant to be generated from HLFIR/FIR. However this approach presents some issues:

1. Some of the location information is difficult to extract from FIR. For example, the location where derived types and its members are declared in the source is not available (or atleast I could not figure out how to get them).

2. Things like fortran module is not a first class entity in IR. One can extract its name from the unique name of the type or variable but this is not ideal.

I have been wondering if it will be better to generate the debug information from AST where we have more complete information. Any ideas or suggestions on this are highly welcome.

Coming back to patch, it does the following:

1. Add bits in the Driver to allow full debug information and maps -g accordingly.
2. In AddDebugFoundationPass, add support to convert mlir::Type to DITypeAttr. The support is limited to scalars, fixed-size arrays and derived types for now.
3. Iterate GlobalOp to generate DIGlobalVariableAttr and attach it to the op.
4. Similarly attach DILocalVariableAttr to AllocaOp or LoadOp.
5. In Codegen, extract these attributes and generate the required MLIR metadata.

Then MLIR to llvm-ir does it work and we get the correct debug information in the executable. At the moment, module variables are being generated as global. DWARF does have the concept of the module that we can use but it is something for the future.


>From 56dffbc43533439073cb2215bdd2e44cec461368 Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Thu, 22 Feb 2024 12:51:42 -0600
Subject: [PATCH] [flang] Improve debug info generation.

This patch adds initial support for generation of debug information for
variables and types. The patch is complete enough to allow inspection
of variables in GDB. The aim is that it can be the basis of discussion
on the design of debug information generation. It will obviously need
more work before it can be considered ready for inclusion.

This patch builds on the existing AddDebugFoundation pass. The existance
of this pass and the DeclareOp seems to suggest that debug information
was meant to be generated from HLFIR/FIR. But this apprach presents some
issues:

1. Some of the location information is difficult to extract from FIR.
For example, the location where derived types and its members are declared
in the source is not available (or atleast I could not figure out how to get
them).

2. Things like fortran module is not a first class entity in IR. One can
extract its name from the unique name of the type or variable but this
is not ideal.

I have been wondering if it will be better to generate the debug information
from AST where we have more complete information. Any ideas or suggestions on
this are highly welcome.

Coming back to patch, it does the following:

1. Driver bits to allow full debug information and maps -g accordingly.
2. In AddDebugFoundationPass, add support to convert mlir::Type to DITypeAttr. The
   support is limited to scalars, fixed-size arrays and derived types.
3. Iterate GlobalOp to generate DIGlobalVariableAttr and attach it to the op.
4. Similarly attach DILocalVariableAttr to AllocaOp or LoadOp.
5. In Codegen, extract these attributes and generate the required MLIR metadata.
---
 clang/lib/Driver/ToolChains/Flang.cpp         |   2 +-
 .../flang/Optimizer/Transforms/Passes.h       |   1 +
 .../flang/Optimizer/Transforms/Passes.td      |   6 +
 flang/include/flang/Tools/CLOptions.inc       |  11 +-
 flang/lib/Frontend/CompilerInvocation.cpp     |   3 +-
 flang/lib/Optimizer/CodeGen/CodeGen.cpp       |  33 +-
 .../Transforms/AddDebugFoundation.cpp         | 429 ++++++++++++++++--
 .../test/Driver/mlir-debug-pass-pipeline.f90  |  14 +-
 .../Transforms/debug-line-table-inc-file.fir  |   2 +-
 flang/test/Transforms/debug-line-table.fir    |  23 +-
 10 files changed, 446 insertions(+), 78 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 6168b42dc78292..f8a2a807c2552c 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -127,7 +127,7 @@ void Flang::addOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
     Arg *gNArg = Args.getLastArg(options::OPT_gN_Group);
     DebugInfoKind = debugLevelToInfoKind(*gNArg);
   } else if (Args.hasArg(options::OPT_g_Flag)) {
-    DebugInfoKind = llvm::codegenoptions::DebugLineTablesOnly;
+    DebugInfoKind = llvm::codegenoptions::FullDebugInfo;
   } else {
     DebugInfoKind = llvm::codegenoptions::NoDebugInfo;
   }
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h
index e1d22c8c986da7..313a94ecf6ac4c 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.h
+++ b/flang/include/flang/Optimizer/Transforms/Passes.h
@@ -64,6 +64,7 @@ std::unique_ptr<mlir::Pass> createMemoryAllocationPass();
 std::unique_ptr<mlir::Pass> createStackArraysPass();
 std::unique_ptr<mlir::Pass> createAliasTagsPass();
 std::unique_ptr<mlir::Pass> createSimplifyIntrinsicsPass();
+std::unique_ptr<mlir::Pass> createAddDebugFoundationPass(unsigned level);
 std::unique_ptr<mlir::Pass> createAddDebugFoundationPass();
 std::unique_ptr<mlir::Pass> createLoopVersioningPass();
 
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index 5fb576fd876254..bd8fbb4a4ef2ed 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -202,6 +202,12 @@ def AddDebugFoundation : Pass<"add-debug-foundation", "mlir::ModuleOp"> {
   let dependentDialects = [
     "fir::FIROpsDialect", "mlir::func::FuncDialect", "mlir::LLVM::LLVMDialect"
   ];
+  let options = [
+    Option<"debugLevel", "debug-level",
+           "unsigned",
+           /*default=*/"0",
+           "Level of debug information to generate.">,
+  ];
 }
 
 // This needs to be a "mlir::ModuleOp" pass, because it inserts simplified
diff --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc
index 68e504d0ccb512..3bc960aafb528d 100644
--- a/flang/include/flang/Tools/CLOptions.inc
+++ b/flang/include/flang/Tools/CLOptions.inc
@@ -153,9 +153,10 @@ inline void addTargetRewritePass(mlir::PassManager &pm) {
   });
 }
 
-inline void addDebugFoundationPass(mlir::PassManager &pm) {
+inline void addDebugFoundationPass(
+    mlir::PassManager &pm, llvm::codegenoptions::DebugInfoKind debugLevel) {
   addPassConditionally(pm, disableDebugFoundation,
-      [&]() { return fir::createAddDebugFoundationPass(); });
+      [&]() { return fir::createAddDebugFoundationPass(debugLevel); });
 }
 
 inline void addFIRToLLVMPass(
@@ -286,7 +287,7 @@ inline void createDebugPasses(
   // Currently only -g1, -g, -gline-tables-only supported
   switch (debugLevel) {
   case llvm::codegenoptions::DebugLineTablesOnly:
-    addDebugFoundationPass(pm);
+    addDebugFoundationPass(pm, debugLevel);
     return;
   case llvm::codegenoptions::NoDebugInfo:
     return;
@@ -294,7 +295,7 @@ inline void createDebugPasses(
     // TODO: Add cases and passes for other debug options.
     // All other debug options not implemented yet, currently emits warning
     // and generates as much debug information as possible.
-    addDebugFoundationPass(pm);
+    addDebugFoundationPass(pm, debugLevel);
     return;
   }
 }
@@ -305,10 +306,10 @@ inline void createDefaultFIRCodeGenPassPipeline(
   pm.addNestedPass<mlir::func::FuncOp>(
       fir::createAbstractResultOnFuncOptPass());
   pm.addNestedPass<fir::GlobalOp>(fir::createAbstractResultOnGlobalOptPass());
+  fir::createDebugPasses(pm, config.DebugInfo);
   fir::addCodeGenRewritePass(pm);
   fir::addTargetRewritePass(pm);
   fir::addExternalNameConversionPass(pm, config.Underscoring);
-  fir::createDebugPasses(pm, config.DebugInfo);
 
   if (config.VScaleMin != 0)
     pm.addPass(fir::createVScaleAttrPass({config.VScaleMin, config.VScaleMax}));
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 4707de0e976ca7..c9cfe4249ed4b6 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -144,8 +144,7 @@ static bool parseDebugArgs(Fortran::frontend::CodeGenOptions &opts,
       return false;
     }
     opts.setDebugInfo(val.value());
-    if (val != llvm::codegenoptions::DebugLineTablesOnly &&
-        val != llvm::codegenoptions::NoDebugInfo) {
+    if (val != llvm::codegenoptions::NoDebugInfo) {
       const auto debugWarning = diags.getCustomDiagID(
           clang::DiagnosticsEngine::Warning, "Unsupported debug option: %0");
       diags.Report(debugWarning) << arg->getValue();
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index f81a08388da722..044725c94f5f3b 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -477,8 +477,15 @@ struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
                   mlir::ConversionPatternRewriter &rewriter) const override {
     auto ty = convertType(addr.getType());
-    rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
+    auto llvmAddrOf = rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
         addr, ty, addr.getSymbol().getRootReference().getValue());
+
+    auto varAttr =
+        addr->getAttrOfType<mlir::LLVM::DILocalVariableAttr>("debug");
+    if (varAttr)
+      rewriter.create<mlir::LLVM::DbgDeclareOp>(llvmAddrOf.getLoc(), llvmAddrOf,
+                                                varAttr, nullptr);
+
     return mlir::success();
   }
 };
@@ -592,6 +599,12 @@ struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> {
     auto llvmAlloc = rewriter.create<mlir::LLVM::AllocaOp>(
         loc, ::getLlvmPtrType(alloc.getContext(), allocaAs), llvmObjectType,
         size);
+    auto varAttr =
+        alloc->getAttrOfType<mlir::LLVM::DILocalVariableAttr>("debug");
+    if (varAttr)
+      rewriter.create<mlir::LLVM::DbgDeclareOp>(llvmAlloc.getLoc(), llvmAlloc,
+                                                varAttr, nullptr);
+
     if (alloc.getPinned())
       llvmAlloc->setDiscardableAttr(alloc.getPinnedAttrName(),
                                     alloc.getPinnedAttr());
@@ -3042,6 +3055,14 @@ struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
   mlir::LogicalResult
   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
                   mlir::ConversionPatternRewriter &rewriter) const override {
+    mlir::LLVM::DIGlobalVariableExpressionAttr dbgExpr;
+
+    auto gvAttr =
+        global->getAttrOfType<mlir::LLVM::DIGlobalVariableAttr>("debug");
+    if (gvAttr)
+      dbgExpr = mlir::LLVM::DIGlobalVariableExpressionAttr::get(
+          global.getContext(), gvAttr, mlir::LLVM::DIExpressionAttr());
+
     auto tyAttr = convertType(global.getType());
     if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(global.getType()))
       tyAttr = this->lowerTy().convertBoxTypeAsStruct(boxType);
@@ -3050,8 +3071,11 @@ struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
     assert(attributeTypeIsCompatible(global.getContext(), initAttr, tyAttr));
     auto linkage = convertLinkage(global.getLinkName());
     auto isConst = global.getConstant().has_value();
+    mlir::SymbolRefAttr comdat;
+    llvm::ArrayRef<mlir::NamedAttribute> attrs;
     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
-        loc, tyAttr, isConst, linkage, global.getSymName(), initAttr);
+        loc, tyAttr, isConst, linkage, global.getSymName(), initAttr, 0, 0,
+        false, false, comdat, attrs, dbgExpr);
 
     auto module = global->getParentOfType<mlir::ModuleOp>();
     // Add comdat if necessary
@@ -3201,6 +3225,11 @@ struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
       else
         attachTBAATag(loadOp, load.getType(), load.getType(), nullptr);
       rewriter.replaceOp(load, loadOp.getResult());
+      auto varAttr =
+          load->getAttrOfType<mlir::LLVM::DILocalVariableAttr>("debug");
+      if (varAttr)
+        rewriter.create<mlir::LLVM::DbgDeclareOp>(
+            loadOp.getLoc(), loadOp.getAddr(), varAttr, nullptr);
     }
     return mlir::success();
   }
diff --git a/flang/lib/Optimizer/Transforms/AddDebugFoundation.cpp b/flang/lib/Optimizer/Transforms/AddDebugFoundation.cpp
index 7a6f58066722d5..20d72ba521d3dc 100644
--- a/flang/lib/Optimizer/Transforms/AddDebugFoundation.cpp
+++ b/flang/lib/Optimizer/Transforms/AddDebugFoundation.cpp
@@ -17,6 +17,8 @@
 #include "flang/Optimizer/Dialect/FIROps.h"
 #include "flang/Optimizer/Dialect/FIRType.h"
 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
+#include "flang/Optimizer/Support/DataLayout.h"
+#include "flang/Optimizer/Support/InternalNames.h"
 #include "flang/Optimizer/Transforms/Passes.h"
 #include "mlir/Dialect/Func/IR/FuncOps.h"
 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
@@ -27,6 +29,7 @@
 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
 #include "mlir/Transforms/RegionUtils.h"
 #include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/Frontend/Debug/Options.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
@@ -44,73 +47,403 @@ namespace {
 class AddDebugFoundationPass
     : public fir::impl::AddDebugFoundationBase<AddDebugFoundationPass> {
 public:
+  AddDebugFoundationPass(unsigned level) { debugLevel = level; }
+  AddDebugFoundationPass() {
+    debugLevel = static_cast<unsigned>(llvm::codegenoptions::FullDebugInfo);
+  }
   void runOnOperation() override;
+
+private:
+  void handleGlobalOp(fir::GlobalOp glocalOp, mlir::LLVM::DIFileAttr fileAttr,
+                      mlir::LLVM::DIScopeAttr scope);
+  void handleFunctionOp(mlir::func::FuncOp funcOp,
+                        mlir::LLVM::DIFileAttr fileAttr,
+                        mlir::LLVM::DICompileUnitAttr cuAttr,
+                        llvm::StringRef parentFilePath);
+  void handleDeclareOp(fir::DeclareOp declOp, mlir::func::FuncOp funcOp,
+                       mlir::LLVM::DIFileAttr fileAttr,
+                       mlir::LLVM::DIScopeAttr scopeAttr, uint32_t &argNo);
+};
+
+class TypeConverter {
+  mlir::LLVM::DITypeAttr convertCharacterType(mlir::MLIRContext *context,
+                                              fir::CharacterType Ty,
+                                              mlir::LLVM::DIFileAttr fileAttr,
+                                              mlir::LLVM::DIScopeAttr scope,
+                                              mlir::Location loc);
+
+  mlir::LLVM::DITypeAttr convertRecordType(mlir::MLIRContext *context,
+                                           fir::RecordType Ty,
+                                           mlir::LLVM::DIFileAttr fileAttr,
+                                           mlir::LLVM::DIScopeAttr scope,
+                                           mlir::Location loc);
+
+  mlir::LLVM::DITypeAttr convertSequenceType(mlir::MLIRContext *context,
+                                             fir::SequenceType Ty,
+                                             mlir::LLVM::DIFileAttr fileAttr,
+                                             mlir::LLVM::DIScopeAttr scope,
+                                             mlir::Location loc);
+
+public:
+  TypeConverter(mlir::ModuleOp m) : module(m) {}
+
+  mlir::LLVM::DITypeAttr convert(mlir::MLIRContext *context, mlir::Type Ty,
+                                 mlir::LLVM::DIFileAttr fileAttr,
+                                 mlir::LLVM::DIScopeAttr scope,
+                                 mlir::Location loc);
+
+private:
+  mlir::ModuleOp module;
 };
 
+static mlir::LLVM::DIEmissionKind getEmissionKind(unsigned debugLevel) {
+  switch (debugLevel) {
+  case llvm::codegenoptions::NoDebugInfo:
+  case llvm::codegenoptions::LocTrackingOnly:
+    return mlir::LLVM::DIEmissionKind::None;
+  case llvm::codegenoptions::DebugLineTablesOnly:
+    return mlir::LLVM::DIEmissionKind::LineTablesOnly;
+  case llvm::codegenoptions::DebugDirectivesOnly:
+  case llvm::codegenoptions::DebugInfoConstructor:
+  case llvm::codegenoptions::LimitedDebugInfo:
+  case llvm::codegenoptions::FullDebugInfo:
+  case llvm::codegenoptions::UnusedTypeInfo:
+    return mlir::LLVM::DIEmissionKind::Full;
+    break;
+  default:
+    assert(false && "Unhandled debug level!");
+  }
+}
+
+static mlir::LLVM::DITypeAttr genPlaceholderType(mlir::MLIRContext *context) {
+  return mlir::LLVM::DIBasicTypeAttr::get(
+      context, llvm::dwarf::DW_TAG_base_type, "void", 32, 1);
+}
+
+static mlir::LLVM::DITypeAttr genBasicType(mlir::MLIRContext *context,
+                                           mlir::StringAttr name,
+                                           unsigned bitSize,
+                                           unsigned decoding) {
+  return mlir::LLVM::DIBasicTypeAttr::get(
+      context, llvm::dwarf::DW_TAG_base_type, name, bitSize, decoding);
+}
+
+static uint32_t getLineFromLoc(mlir::Location loc) {
+  uint32_t line = 1;
+  if (auto fileLoc = loc.dyn_cast<mlir::FileLineColLoc>())
+    line = fileLoc.getLine();
+  return line;
+}
+
+static uint32_t getTypeSize(mlir::LLVM::DITypeAttr Ty, mlir::Location loc) {
+  if (auto MT = Ty.dyn_cast_or_null<mlir::LLVM::DIBasicTypeAttr>())
+    return MT.getSizeInBits();
+  else if (auto MT = Ty.dyn_cast_or_null<mlir::LLVM::DIDerivedTypeAttr>())
+    return MT.getSizeInBits();
+  else if (auto MT = Ty.dyn_cast_or_null<mlir::LLVM::DICompositeTypeAttr>())
+    return MT.getSizeInBits();
+  TODO(loc, "Unsupported type!");
+}
+
 } // namespace
 
-void AddDebugFoundationPass::runOnOperation() {
+mlir::LLVM::DITypeAttr TypeConverter::convertCharacterType(
+    mlir::MLIRContext *context, fir::CharacterType Ty,
+    mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DIScopeAttr scope,
+    mlir::Location loc) {
+
+  if (!Ty.hasConstantLen())
+    return genPlaceholderType(context);
+
+  fir::CharacterType::LenType len = Ty.getLen();
+  auto charTy =
+      genBasicType(context, mlir::StringAttr::get(context, Ty.getMnemonic()),
+                   Ty.getFKind() * 8, llvm::dwarf::DW_ATE_unsigned_char);
+
+  if (len == 1)
+    return charTy;
+
+  auto intTy = mlir::IntegerType::get(context, 64);
+  auto countAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, len));
+  auto lowerAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, 1));
+  auto subrangeTy = mlir::LLVM::DISubrangeAttr::get(
+      context, countAttr, lowerAttr, nullptr, nullptr);
+
+  return mlir::LLVM::DICompositeTypeAttr::get(
+      context, llvm::dwarf::DW_TAG_array_type,
+      mlir::StringAttr::get(context, ""), fileAttr, getLineFromLoc(loc), scope,
+      charTy, mlir::LLVM::DIFlags::Zero, len * getTypeSize(charTy, loc),
+      /*alignInBits*/ 0, {subrangeTy});
+}
+
+mlir::LLVM::DITypeAttr
+TypeConverter::convertRecordType(mlir::MLIRContext *context, fir::RecordType Ty,
+                                 mlir::LLVM::DIFileAttr fileAttr,
+                                 mlir::LLVM::DIScopeAttr scope,
+                                 mlir::Location loc) {
+
+  llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
+  // The offset for the fields is being calculated by adding
+  // the bit sizes of all the previous fields.
+  uint64_t offset = 0;
+
+  for (auto CT : Ty.getTypeList()) {
+    auto MT =
+        convert(context, fir::unwrapRefType(CT.second), fileAttr, scope, loc);
+    uint64_t BitSize = getTypeSize(MT, loc);
+    auto DT = mlir::LLVM::DIDerivedTypeAttr::get(
+        context, llvm::dwarf::DW_TAG_member,
+        mlir::StringAttr::get(context, CT.first), MT, BitSize,
+        /*alignInBits*/ 0, offset);
+    elements.push_back(DT);
+    offset += BitSize;
+  }
+
+  // TODO: Handle parent type
+  mlir::LLVM::DITypeAttr parentTy = mlir::LLVM::DINullTypeAttr::get(context);
+
+  // TODO: The RecordType and FieldType does not seem to have location. So
+  // at the moment, we are forced to use whatever location the variable was
+  // declared.
+  return mlir::LLVM::DICompositeTypeAttr::get(
+      context, llvm::dwarf::DW_TAG_structure_type,
+      mlir::StringAttr::get(context, Ty.getName()), fileAttr,
+      getLineFromLoc(loc), scope, parentTy, mlir::LLVM::DIFlags::Zero, offset,
+      /*alignInBits*/ 0, elements);
+}
+
+mlir::LLVM::DITypeAttr TypeConverter::convertSequenceType(
+    mlir::MLIRContext *context, fir::SequenceType seqTy,
+    mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DIScopeAttr scope,
+    mlir::Location loc) {
+
+  // TODO: Only fixed sizes arrays handled at the moment.
+  if (seqTy.hasDynamicExtents())
+    return genPlaceholderType(context);
+
+  llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
+  uint64_t size = 0;
+  auto elemTy = convert(context, seqTy.getEleTy(), fileAttr, scope, loc);
+  for (auto dim : seqTy.getShape()) {
+    size += dim;
+    auto intTy = mlir::IntegerType::get(context, 64);
+    // TODO: Only supporting lower bound of 1 at the moment.
+    auto countAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, dim));
+    auto lowerAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, 1));
+    auto subrangeTy = mlir::LLVM::DISubrangeAttr::get(
+        context, countAttr, lowerAttr, nullptr, nullptr);
+    elements.push_back(subrangeTy);
+  }
+  return mlir::LLVM::DICompositeTypeAttr::get(
+      context, llvm::dwarf::DW_TAG_array_type,
+      mlir::StringAttr::get(context, ""), fileAttr, getLineFromLoc(loc), scope,
+      elemTy, mlir::LLVM::DIFlags::Zero, size * getTypeSize(elemTy, loc),
+      /*alignInBits*/ 0, elements);
+}
+
+mlir::LLVM::DITypeAttr TypeConverter::convert(mlir::MLIRContext *context,
+                                              mlir::Type Ty,
+                                              mlir::LLVM::DIFileAttr fileAttr,
+                                              mlir::LLVM::DIScopeAttr scope,
+                                              mlir::Location loc) {
+  fir::KindMapping kindMap = fir::getKindMapping(module);
+  if (Ty.isIntOrIndex()) {
+    return genBasicType(context, mlir::StringAttr::get(context, "integer"),
+                        Ty.getIntOrFloatBitWidth(), llvm::dwarf::DW_ATE_signed);
+  } else if (Ty.isa<mlir::FloatType>() || Ty.isa<fir::RealType>()) {
+    return genBasicType(context, mlir::StringAttr::get(context, "real"),
+                        Ty.getIntOrFloatBitWidth(), llvm::dwarf::DW_ATE_float);
+  } else if (auto logTy = Ty.dyn_cast_or_null<fir::LogicalType>()) {
+    return genBasicType(context,
+                        mlir::StringAttr::get(context, logTy.getMnemonic()),
+                        kindMap.getLogicalBitsize(logTy.getFKind()),
+                        llvm::dwarf::DW_ATE_boolean);
+  } else if (fir::isa_complex(Ty)) {
+    unsigned bitWidth;
+    if (auto cplxTy = mlir::dyn_cast_or_null<mlir::ComplexType>(Ty)) {
+      auto floatTy = cplxTy.getElementType().cast<mlir::FloatType>();
+      bitWidth = floatTy.getWidth();
+    } else if (auto cplxTy = mlir::dyn_cast_or_null<fir::ComplexType>(Ty)) {
+      bitWidth = kindMap.getRealBitsize(cplxTy.getFKind());
+    }
+    return genBasicType(context, mlir::StringAttr::get(context, "complex"),
+                        bitWidth * 2, llvm::dwarf::DW_ATE_complex_float);
+  } else if (auto charTy = Ty.dyn_cast_or_null<fir::CharacterType>()) {
+    return convertCharacterType(context, charTy, fileAttr, scope, loc);
+  } else if (auto recTy = Ty.dyn_cast_or_null<fir::RecordType>()) {
+    return convertRecordType(context, recTy, fileAttr, scope, loc);
+  } else if (auto seqTy = Ty.dyn_cast_or_null<fir::SequenceType>()) {
+    return convertSequenceType(context, seqTy, fileAttr, scope, loc);
+  } else if (Ty.isa<fir::PointerType>() || Ty.isa<fir::ReferenceType>() ||
+             Ty.isa<fir::BoxType>() || Ty.isa<mlir::TupleType>()) {
+    // TODO: These types are currently unhandled. We are generating a
+    // placeholder type to allow us to test supported bits.
+    return genPlaceholderType(context);
+  } else
+    TODO(loc, "Unsupported Type!");
+}
+
+void AddDebugFoundationPass::handleGlobalOp(fir::GlobalOp globalOp,
+                                            mlir::LLVM::DIFileAttr fileAttr,
+                                            mlir::LLVM::DIScopeAttr scope) {
+  mlir::ModuleOp module = getOperation();
+  mlir::MLIRContext *context = &getContext();
+  TypeConverter tyConverter(module);
+
+  auto result = fir::NameUniquer::deconstruct(globalOp.getSymName());
+  // TODO: Use module information from the 'result' if available
+  auto diType =
+      tyConverter.convert(context, fir::unwrapRefType(globalOp.getType()),
+                          fileAttr, scope, globalOp.getLoc());
+
+  auto gvAttr = mlir::LLVM::DIGlobalVariableAttr::get(
+      context, scope, mlir::StringAttr::get(context, result.second.name),
+      mlir::StringAttr::get(context, globalOp.getName()), fileAttr,
+      getLineFromLoc(globalOp.getLoc()), diType, /*isLocalToUnit*/ true,
+      /*isDefinition*/ true, /* alignInBits*/ 0);
+  globalOp->setAttr("debug", gvAttr);
+}
+
+void AddDebugFoundationPass::handleDeclareOp(fir::DeclareOp declOp,
+                                             mlir::func::FuncOp funcOp,
+                                             mlir::LLVM::DIFileAttr fileAttr,
+                                             mlir::LLVM::DIScopeAttr scopeAttr,
+                                             uint32_t &argNo) {
   mlir::ModuleOp module = getOperation();
   mlir::MLIRContext *context = &getContext();
+  TypeConverter tyConverter(module);
+
+  auto refOp = declOp.getMemref();
+  bool isLocal = refOp.getDefiningOp();
+  auto diType =
+      tyConverter.convert(context, fir::unwrapRefType(declOp.getType()),
+                          fileAttr, scopeAttr, declOp.getLoc());
+  auto result = fir::NameUniquer::deconstruct(declOp.getUniqName());
+  auto localVarAttr = mlir::LLVM::DILocalVariableAttr::get(
+      context, scopeAttr, mlir::StringAttr::get(context, result.second.name),
+      fileAttr, getLineFromLoc(declOp.getLoc()), isLocal ? 0 : argNo++,
+      /* alignInBits*/ 0, diType);
+
+  if (isLocal)
+    refOp.getDefiningOp()->setAttr("debug", localVarAttr);
+  else {
+    if (auto arg = mlir::dyn_cast_or_null<mlir::BlockArgument>(refOp)) {
+      bool done = false;
+      // find the LoadOp that loads the block argument and attach local
+      // variable attribute to it.
+      funcOp.walk([&](fir::LoadOp loadOp) {
+        if (done)
+          return;
+        if (loadOp.getMemref() == declOp) {
+          done = true;
+          loadOp->setAttr("debug", localVarAttr);
+        }
+      });
+    }
+  }
+}
+
+void AddDebugFoundationPass::handleFunctionOp(
+    mlir::func::FuncOp funcOp, mlir::LLVM::DIFileAttr fileAttr,
+    mlir::LLVM::DICompileUnitAttr cuAttr, llvm::StringRef parentFilePath) {
+  mlir::ModuleOp module = getOperation();
+  mlir::MLIRContext *context = &getContext();
+  TypeConverter tyConverter(module);
   mlir::OpBuilder builder(context);
+
+  mlir::Location l = funcOp->getLoc();
+  // If fused location has already been created then nothing to do
+  // Otherwise, create a fused location.
+  if (l.dyn_cast<mlir::FusedLoc>())
+    return;
+
+  llvm::StringRef funcFilePath{parentFilePath};
+  unsigned funcLine = 1;
+  if (auto funcLoc = l.dyn_cast<mlir::FileLineColLoc>()) {
+    funcLine = funcLoc.getLine();
+    funcFilePath = funcLoc.getFilename().getValue();
+  }
+
+  mlir::StringAttr funcName = mlir::StringAttr::get(context, funcOp.getName());
+  llvm::SmallVector<mlir::LLVM::DITypeAttr> types;
+  for (auto resTy : funcOp.getResultTypes()) {
+    auto tyAttr =
+        tyConverter.convert(context, resTy, fileAttr, cuAttr, funcOp.getLoc());
+    types.push_back(tyAttr);
+  }
+  for (auto inTy : funcOp.getArgumentTypes()) {
+    auto tyAttr =
+        tyConverter.convert(context, inTy, fileAttr, cuAttr, funcOp.getLoc());
+    types.push_back(tyAttr);
+  }
+  mlir::LLVM::DISubroutineTypeAttr subTypeAttr =
+      mlir::LLVM::DISubroutineTypeAttr::get(
+          context, llvm::dwarf::getCallingConvention("DW_CC_normal"), types);
+
+  mlir::LLVM::DIFileAttr funcFileAttr = mlir::LLVM::DIFileAttr::get(
+      context, llvm::sys::path::filename(funcFilePath),
+      llvm::sys::path::parent_path(funcFilePath));
+
+  // Only definitions need a distinct identifier and a compilation unit.
+  mlir::DistinctAttr id;
+  mlir::LLVM::DICompileUnitAttr compilationUnit;
+  auto subprogramFlags = mlir::LLVM::DISubprogramFlags::Optimized;
+  if (!funcOp.isExternal()) {
+    id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
+    compilationUnit = cuAttr;
+    subprogramFlags =
+        subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;
+  }
+  auto spAttr = mlir::LLVM::DISubprogramAttr::get(
+      context, id, compilationUnit, fileAttr, funcName, funcName, funcFileAttr,
+      /*line=*/funcLine, /*scopeline=*/funcLine, subprogramFlags, subTypeAttr);
+  funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr));
+
+  // We have done enough for the line table. Process variables only
+  // if full debug info is required.
+  if (debugLevel != llvm::codegenoptions::FullDebugInfo)
+    return;
+
+  uint32_t argNo = 1;
+  funcOp.walk([&](fir::DeclareOp declOp) {
+    handleDeclareOp(declOp, funcOp, fileAttr, spAttr, argNo);
+  });
+}
+
+void AddDebugFoundationPass::runOnOperation() {
+  mlir::ModuleOp module = getOperation();
+  mlir::MLIRContext *context = &getContext();
   std::string inputFilePath("-");
+  TypeConverter tyConverter(module);
   if (auto fileLoc = module.getLoc().dyn_cast<mlir::FileLineColLoc>())
     inputFilePath = fileLoc.getFilename().getValue();
 
-  auto getFileAttr = [context](llvm::StringRef path) -> mlir::LLVM::DIFileAttr {
-    return mlir::LLVM::DIFileAttr::get(context, llvm::sys::path::filename(path),
-                                       llvm::sys::path::parent_path(path));
-  };
-
-  mlir::LLVM::DIFileAttr fileAttr = getFileAttr(inputFilePath);
   mlir::StringAttr producer = mlir::StringAttr::get(context, "Flang");
+  mlir::LLVM::DIFileAttr fileAttr = mlir::LLVM::DIFileAttr::get(
+      context, llvm::sys::path::filename(inputFilePath),
+      llvm::sys::path::parent_path(inputFilePath));
+
   mlir::LLVM::DICompileUnitAttr cuAttr = mlir::LLVM::DICompileUnitAttr::get(
       context, mlir::DistinctAttr::create(mlir::UnitAttr::get(context)),
       llvm::dwarf::getLanguage("DW_LANG_Fortran95"), fileAttr, producer,
-      /*isOptimized=*/false, mlir::LLVM::DIEmissionKind::LineTablesOnly);
+      /*isOptimized=*/false, getEmissionKind(debugLevel));
 
   module.walk([&](mlir::func::FuncOp funcOp) {
-    mlir::Location l = funcOp->getLoc();
-    // If fused location has already been created then nothing to do
-    // Otherwise, create a fused location.
-    if (l.dyn_cast<mlir::FusedLoc>())
-      return;
-
-    llvm::StringRef funcFilePath;
-    if (l.dyn_cast<mlir::FileLineColLoc>())
-      funcFilePath =
-          l.dyn_cast<mlir::FileLineColLoc>().getFilename().getValue();
-    else
-      funcFilePath = inputFilePath;
-
-    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::DIFileAttr funcFileAttr = getFileAttr(funcFilePath);
-
-    // Only definitions need a distinct identifier and a compilation unit.
-    mlir::DistinctAttr id;
-    mlir::LLVM::DICompileUnitAttr compilationUnit;
-    auto subprogramFlags = mlir::LLVM::DISubprogramFlags::Optimized;
-    if (!funcOp.isExternal()) {
-      id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
-      compilationUnit = cuAttr;
-      subprogramFlags =
-          subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;
-    }
-    auto spAttr = mlir::LLVM::DISubprogramAttr::get(
-        context, id, compilationUnit, fileAttr, funcName, funcName,
-        funcFileAttr,
-        /*line=*/1,
-        /*scopeline=*/1, subprogramFlags, subTypeAttr);
-    funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr));
+    handleFunctionOp(funcOp, fileAttr, cuAttr, inputFilePath);
   });
+
+  // Process GlobalOp only if full debug info is required.
+  if (debugLevel != llvm::codegenoptions::FullDebugInfo)
+    return;
+
+  module.walk([&](fir::GlobalOp globalOp) {
+    handleGlobalOp(globalOp, fileAttr, cuAttr);
+  });
+}
+
+std::unique_ptr<mlir::Pass>
+fir::createAddDebugFoundationPass(unsigned debugLevel) {
+  return std::make_unique<AddDebugFoundationPass>(debugLevel);
 }
 
 std::unique_ptr<mlir::Pass> fir::createAddDebugFoundationPass() {
diff --git a/flang/test/Driver/mlir-debug-pass-pipeline.f90 b/flang/test/Driver/mlir-debug-pass-pipeline.f90
index 45b1717d7187db..e4f5c5288339ad 100644
--- a/flang/test/Driver/mlir-debug-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-debug-pass-pipeline.f90
@@ -6,9 +6,9 @@
 ! RUN: %flang -g -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL,DEBUG %s
 ! RUN: %flang -g1 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL,DEBUG %s
 ! RUN: %flang -gline-tables-only -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL,DEBUG %s
-! RUN: %flang -gline-directives-only -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL,DEBUG,DEBUG-DIRECTIVES %s
-! RUN: %flang -g2 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL,DEBUG,DEBUG-CONSTRUCT %s
-! RUN: %flang -g3 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL,DEBUG,DEBUG-CONSTRUCT %s
+! RUN: %flang -gline-directives-only -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL %s
+! RUN: %flang -g2 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL %s
+! RUN: %flang -g3 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL %s
 
 ! RUN: not %flang_fc1 -debug-info-kind=invalid -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o /dev/null 2>&1 | FileCheck --check-prefixes=DEBUG-ERR %s
 
@@ -16,8 +16,6 @@
 
 end program
 
-! DEBUG-CONSTRUCT: warning: Unsupported debug option: constructor
-! DEBUG-DIRECTIVES: warning: Unsupported debug option: line-directives-only
 !
 ! DEBUG-ERR: error: invalid value 'invalid' in '-debug-info-kind=invalid'
 ! DEBUG-ERR-NOT: Pass statistics report
@@ -76,11 +74,11 @@
 ! ALL-NEXT:  'func.func' Pipeline
 ! ALL-NEXT:    AbstractResultOnFuncOpt
 
-! ALL-NEXT: CodeGenRewrite
+! DEBUG-NEXT: AddDebugFoundation
+! NO-DEBUG-NOT: AddDebugFoundation
+! ALL: CodeGenRewrite
 ! ALL-NEXT:   (S) 0 num-dce'd - Number of operations eliminated
 ! ALL-NEXT: TargetRewrite
 ! ALL-NEXT: ExternalNameConversion
-! DEBUG-NEXT: AddDebugFoundation
-! NO-DEBUG-NOT: AddDebugFoundation
 ! ALL: FIRToLLVMLowering
 ! ALL-NOT: LLVMIRLoweringPass
diff --git a/flang/test/Transforms/debug-line-table-inc-file.fir b/flang/test/Transforms/debug-line-table-inc-file.fir
index f809ab99b47279..9e0f1ee5c49d28 100644
--- a/flang/test/Transforms/debug-line-table-inc-file.fir
+++ b/flang/test/Transforms/debug-line-table-inc-file.fir
@@ -30,7 +30,7 @@ module attributes {} {
 // CHECK: #[[MODULE_LOC]] = loc("{{.*}}simple.f90":0:0)
 // CHECK: #[[LOC_INC_FILE:.*]] = loc("{{.*}}inc.f90":1:1)
 // CHECK: #[[LOC_FILE:.*]] = loc("{{.*}}simple.f90":3:1)
-// CHECK: #[[DI_CU:.*]] = #llvm.di_compile_unit<id = distinct[{{.*}}]<>, sourceLanguage = DW_LANG_Fortran95, file = #[[DI_FILE]], producer = "Flang", isOptimized = false, emissionKind = LineTablesOnly>
+// CHECK: #[[DI_CU:.*]] = #llvm.di_compile_unit<id = distinct[{{.*}}]<>, sourceLanguage = DW_LANG_Fortran95, file = #[[DI_FILE]], producer = "Flang", isOptimized = false, emissionKind = Full>
 // CHECK: #[[DI_SP_INC:.*]] = #llvm.di_subprogram<id = distinct[{{.*}}]<>, compileUnit = #[[DI_CU]], scope = #[[DI_FILE]], name = "_QPsinc", linkageName = "_QPsinc", file = #[[DI_INC_FILE]], {{.*}}>
 // CHECK: #[[DI_SP:.*]] = #llvm.di_subprogram<id = distinct[{{.*}}]<>, compileUnit = #[[DI_CU]], scope = #[[DI_FILE]], name = "_QQmain", linkageName = "_QQmain", file = #[[DI_FILE]], {{.*}}>
 // CHECK: #[[FUSED_LOC_INC_FILE]] = loc(fused<#[[DI_SP_INC]]>[#[[LOC_INC_FILE]]])
diff --git a/flang/test/Transforms/debug-line-table.fir b/flang/test/Transforms/debug-line-table.fir
index 8e66fc1ab39889..bafe98bf396b39 100644
--- a/flang/test/Transforms/debug-line-table.fir
+++ b/flang/test/Transforms/debug-line-table.fir
@@ -17,14 +17,15 @@ module attributes { fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.dat
 // CHECK:   } loc(#[[FUSED_SB_LOC:.*]])
 // CHECK:   func.func private @[[DECL_NAME:.*]]() -> i32 loc(#[[FUSED_DECL_LOC:.*]])
 // CHECK: } loc(#[[MODULE_LOC:.*]])
-// CHECK: #di_basic_type = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "void", encoding = DW_ATE_address>
-// 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: #[[DECL_LOC:.*]] = loc("./simple.f90":10:1)
-// CHECK: #di_compile_unit = #llvm.di_compile_unit<id = distinct[{{.*}}]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, producer = "Flang", isOptimized = false, emissionKind = LineTablesOnly>
-// CHECK: #di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, types = #di_basic_type, #di_basic_type>
-// CHECK: #[[SB_SUBPROGRAM:.*]] = #llvm.di_subprogram<id = distinct[{{.*}}]<>, compileUnit = #di_compile_unit, scope = #di_file, name = "[[SB_NAME]]", linkageName = "[[SB_NAME]]", file = #di_file, line = 1, scopeLine = 1, subprogramFlags = "Definition|Optimized", type = #di_subroutine_type>
-// CHECK: #[[DECL_SUBPROGRAM:.*]] = #llvm.di_subprogram<scope = #di_file, name = "[[DECL_NAME]]", linkageName = "[[DECL_NAME]]", file = #di_file, line = 1, scopeLine = 1, subprogramFlags = Optimized, type = #di_subroutine_type>
-// CHECK: #[[FUSED_SB_LOC]] = loc(fused<#[[SB_SUBPROGRAM]]>[#[[SB_LOC]]])
-// CHECK: #[[FUSED_DECL_LOC]] = loc(fused<#[[DECL_SUBPROGRAM]]>[#[[DECL_LOC]]])
+// CHECK-DAG: #di_basic_type = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "integer", sizeInBits = 32, encoding = DW_ATE_signed>
+// CHECK-DAG: #di_file = #llvm.di_file<"[[FILE_NAME:.*]]" in "[[DIR_NAME:.*]]">
+// CHECK-DAG: #[[MODULE_LOC]] = loc("[[DIR_NAME]]/[[FILE_NAME]]":1:1)
+// CHECK-DAG: #[[SB_LOC]] = loc("./simple.f90":2:1)
+// CHECK-DAG: #[[DECL_LOC:.*]] = loc("./simple.f90":10:1)
+// CHECK-DAG: #di_compile_unit = #llvm.di_compile_unit<id = distinct[{{.*}}]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, producer = "Flang", isOptimized = false, emissionKind = Full>
+// CHECK-DAG: #di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
+// CHECK-DAG: #di_subroutine_type1 = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, types = #di_basic_type>
+// CHECK-DAG: #[[SB_SUBPROGRAM:.*]] = #llvm.di_subprogram<id = distinct[{{.*}}]<>, compileUnit = #di_compile_unit, scope = #di_file, name = "[[SB_NAME]]", linkageName = "[[SB_NAME]]", file = #di_file, line = 2, scopeLine = 2, subprogramFlags = "Definition|Optimized", type = #di_subroutine_type>
+// CHECK-DAG: #[[DECL_SUBPROGRAM:.*]] = #llvm.di_subprogram<scope = #di_file, name = "[[DECL_NAME]]", linkageName = "[[DECL_NAME]]", file = #di_file, line = 10, scopeLine = 10, subprogramFlags = Optimized, type = #di_subroutine_type1>
+// CHECK-DAG: #[[FUSED_SB_LOC]] = loc(fused<#[[SB_SUBPROGRAM]]>[#[[SB_LOC]]])
+// CHECK-DAG: #[[FUSED_DECL_LOC]] = loc(fused<#[[DECL_SUBPROGRAM]]>[#[[DECL_LOC]]])



More information about the flang-commits mailing list