[mlir][llvm] Import additional debug info from LLVMIR.
authorTobias Gysi <tobias.gysi@nextsilicon.com>
Fri, 18 Nov 2022 08:44:26 +0000 (09:44 +0100)
committerTobias Gysi <tobias.gysi@nextsilicon.com>
Fri, 18 Nov 2022 08:44:40 +0000 (09:44 +0100)
Add a DebugImporter to convert LLVMIR debug metadata into
MLIR debug attributes. It is the counterpart to the
DebugTranslation class and supports the same attributes.
The revision only supports the translation of instruction,
function, and module debug information. The import of
intrinsics is left to a later revision.

Depends on D138206

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D138209

mlir/lib/Target/LLVMIR/CMakeLists.txt
mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp
mlir/lib/Target/LLVMIR/DebugImporter.cpp [new file with mode: 0644]
mlir/lib/Target/LLVMIR/DebugImporter.h [new file with mode: 0644]
mlir/test/Target/LLVMIR/Import/debug-info.ll

index 3ee8382..caf3621 100644 (file)
@@ -4,6 +4,7 @@ set(LLVM_OPTIONAL_SOURCES
   ConvertFromLLVMIR.cpp
   ConvertToLLVMIR.cpp
   DebugTranslation.cpp
+  DebugImporter.cpp
   ModuleTranslation.cpp
   TypeToLLVM.cpp
   TypeFromLLVM.cpp
@@ -50,6 +51,7 @@ add_mlir_translation_library(MLIRToLLVMIRTranslationRegistration
 
 add_mlir_translation_library(MLIRTargetLLVMIRImport
   ConvertFromLLVMIR.cpp
+  DebugImporter.cpp
   TypeFromLLVM.cpp
 
   ADDITIONAL_HEADER_DIRS
index 18cff0c..78b5e22 100644 (file)
@@ -10,6 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "DebugImporter.h"
 #include "mlir/Target/LLVMIR/Import.h"
 
 #include "mlir/Dialect/DLTI/DLTI.h"
@@ -42,6 +43,7 @@
 
 using namespace mlir;
 using namespace mlir::LLVM;
+using mlir::LLVM::detail::DebugImporter;
 
 #include "mlir/Dialect/LLVMIR/LLVMConversionEnumsFromLLVM.inc"
 
@@ -329,7 +331,7 @@ class Importer {
 public:
   Importer(MLIRContext *context, ModuleOp module)
       : builder(context), context(context), module(module),
-        typeTranslator(*context) {
+        typeTranslator(*context), debugImporter(context) {
     builder.setInsertionPointToStart(module.getBody());
   }
 
@@ -369,12 +371,15 @@ public:
   /// Converts `value` to an integer attribute. Asserts if the conversion fails.
   IntegerAttr matchIntegerAttr(Value value);
 
-  /// Translate the debug location to a FileLineColLoc, if `loc` is non-null.
-  /// Otherwise, return UnknownLoc.
-  Location translateLoc(llvm::DILocation *loc);
+  /// Translates the debug location.
+  Location translateLoc(llvm::DILocation *loc) {
+    return debugImporter.translateLoc(loc);
+  }
 
   /// Converts the type from LLVM to MLIR LLVM dialect.
-  Type convertType(llvm::Type *type);
+  Type convertType(llvm::Type *type) {
+    return typeTranslator.translateType(type);
+  }
 
   /// Converts an LLVM intrinsic to an MLIR LLVM dialect operation if an MLIR
   /// counterpart exists. Otherwise, returns failure.
@@ -450,21 +455,11 @@ private:
   DenseMap<llvm::GlobalVariable *, GlobalOp> globals;
   /// The stateful type translator (contains named structs).
   LLVM::TypeFromLLVMIRTranslator typeTranslator;
+  /// Stateful debug information importer.
+  DebugImporter debugImporter;
 };
 } // namespace
 
-Location Importer::translateLoc(llvm::DILocation *loc) {
-  if (!loc)
-    return UnknownLoc::get(context);
-
-  return FileLineColLoc::get(context, loc->getFilename(), loc->getLine(),
-                             loc->getColumn());
-}
-
-Type Importer::convertType(llvm::Type *type) {
-  return typeTranslator.translateType(type);
-}
-
 LogicalResult Importer::convertIntrinsic(OpBuilder &odsBuilder,
                                          llvm::CallInst *inst) {
   // Check if the callee is an intrinsic.
@@ -1064,6 +1059,9 @@ LogicalResult Importer::processFunction(llvm::Function *func) {
       UnknownLoc::get(context), func->getName(), functionType,
       convertLinkageFromLLVM(func->getLinkage()), dsoLocal, cconv);
 
+  // Set the function debug information if available.
+  debugImporter.translate(func, funcOp);
+
   for (const auto &it : llvm::enumerate(functionType.getParams())) {
     llvm::SmallVector<NamedAttribute, 1> argAttrs;
     if (auto *type = func->getParamByValType(it.index())) {
@@ -1150,8 +1148,9 @@ mlir::translateLLVMIRToModule(std::unique_ptr<llvm::Module> llvmModule,
                               MLIRContext *context) {
   context->loadDialect<LLVMDialect>();
   context->loadDialect<DLTIDialect>();
-  OwningOpRef<ModuleOp> module(ModuleOp::create(
-      FileLineColLoc::get(context, "", /*line=*/0, /*column=*/0)));
+  OwningOpRef<ModuleOp> module(ModuleOp::create(FileLineColLoc::get(
+      StringAttr::get(context, llvmModule->getSourceFileName()), /*line=*/0,
+      /*column=*/0)));
 
   DataLayoutSpecInterface dlSpec =
       translateDataLayout(llvmModule->getDataLayout(), context);
diff --git a/mlir/lib/Target/LLVMIR/DebugImporter.cpp b/mlir/lib/Target/LLVMIR/DebugImporter.cpp
new file mode 100644 (file)
index 0000000..cd1f58f
--- /dev/null
@@ -0,0 +1,209 @@
+//===- DebugImporter.cpp - LLVM to MLIR Debug conversion ------------------===//
+//
+// 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 "DebugImporter.h"
+#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
+#include "mlir/IR/Attributes.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/Location.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace mlir;
+using namespace mlir::LLVM;
+using namespace mlir::LLVM::detail;
+
+void DebugImporter::translate(llvm::Function *func, LLVMFuncOp funcOp) {
+  if (!func->getSubprogram())
+    return;
+
+  // Add a fused location to link the subprogram information.
+  StringAttr name = StringAttr::get(context, func->getSubprogram()->getName());
+  funcOp->setLoc(FusedLocWith<DISubprogramAttr>::get(
+      {NameLoc::get(name)}, translate(func->getSubprogram()), context));
+}
+
+//===----------------------------------------------------------------------===//
+// Attributes
+//===----------------------------------------------------------------------===//
+
+DIBasicTypeAttr DebugImporter::translateImpl(llvm::DIBasicType *node) {
+  return DIBasicTypeAttr::get(context, node->getTag(), node->getName(),
+                              node->getSizeInBits(), node->getEncoding());
+}
+
+DICompileUnitAttr DebugImporter::translateImpl(llvm::DICompileUnit *node) {
+  Optional<DIEmissionKind> emissionKind =
+      symbolizeDIEmissionKind(node->getEmissionKind());
+  return DICompileUnitAttr::get(context, node->getSourceLanguage(),
+                                translate(node->getFile()),
+                                StringAttr::get(context, node->getProducer()),
+                                node->isOptimized(), emissionKind.value());
+}
+
+DICompositeTypeAttr DebugImporter::translateImpl(llvm::DICompositeType *node) {
+  Optional<DIFlags> flags = symbolizeDIFlags(node->getFlags());
+  SmallVector<DINodeAttr> elements;
+  for (llvm::DINode *element : node->getElements()) {
+    assert(element && "expected a non-null element type");
+    elements.push_back(translate(element));
+  }
+  return DICompositeTypeAttr::get(
+      context, node->getTag(), StringAttr::get(context, node->getName()),
+      translate(node->getFile()), node->getLine(), translate(node->getScope()),
+      translate(node->getBaseType()), flags.value_or(DIFlags::Zero),
+      node->getSizeInBits(), node->getAlignInBits(), elements);
+}
+
+DIDerivedTypeAttr DebugImporter::translateImpl(llvm::DIDerivedType *node) {
+  return DIDerivedTypeAttr::get(
+      context, node->getTag(),
+      node->getRawName() ? StringAttr::get(context, node->getName()) : nullptr,
+      translate(node->getBaseType()), node->getSizeInBits(),
+      node->getAlignInBits(), node->getOffsetInBits());
+}
+
+DIFileAttr DebugImporter::translateImpl(llvm::DIFile *node) {
+  return DIFileAttr::get(context, node->getFilename(), node->getDirectory());
+}
+
+DILexicalBlockAttr DebugImporter::translateImpl(llvm::DILexicalBlock *node) {
+  return DILexicalBlockAttr::get(context, translate(node->getScope()),
+                                 translate(node->getFile()), node->getLine(),
+                                 node->getColumn());
+}
+
+DILexicalBlockFileAttr
+DebugImporter::translateImpl(llvm::DILexicalBlockFile *node) {
+  return DILexicalBlockFileAttr::get(context, translate(node->getScope()),
+                                     translate(node->getFile()),
+                                     node->getDiscriminator());
+}
+
+DILocalVariableAttr DebugImporter::translateImpl(llvm::DILocalVariable *node) {
+  return DILocalVariableAttr::get(context, translate(node->getScope()),
+                                  StringAttr::get(context, node->getName()),
+                                  translate(node->getFile()), node->getLine(),
+                                  node->getArg(), node->getAlignInBits(),
+                                  translate(node->getType()));
+}
+
+DIScopeAttr DebugImporter::translateImpl(llvm::DIScope *node) {
+  return cast<DIScopeAttr>(translate(static_cast<llvm::DINode *>(node)));
+}
+
+DISubprogramAttr DebugImporter::translateImpl(llvm::DISubprogram *node) {
+  Optional<DISubprogramFlags> subprogramFlags =
+      symbolizeDISubprogramFlags(node->getSubprogram()->getSPFlags());
+  return DISubprogramAttr::get(
+      context, translate(node->getUnit()), translate(node->getScope()),
+      StringAttr::get(context, node->getName()),
+      node->getRawLinkageName()
+          ? StringAttr::get(context, node->getLinkageName())
+          : nullptr,
+      translate(node->getFile()), node->getLine(), node->getScopeLine(),
+      subprogramFlags.value(), translate(node->getType()));
+}
+
+DISubrangeAttr DebugImporter::translateImpl(llvm::DISubrange *node) {
+  auto getIntegerAttrOrNull = [&](llvm::DISubrange::BoundType data) {
+    if (auto *constInt = llvm::dyn_cast_or_null<llvm::ConstantInt *>(data))
+      return IntegerAttr::get(IntegerType::get(context, 64),
+                              constInt->getSExtValue());
+    return IntegerAttr();
+  };
+  return DISubrangeAttr::get(context, getIntegerAttrOrNull(node->getCount()),
+                             getIntegerAttrOrNull(node->getLowerBound()),
+                             getIntegerAttrOrNull(node->getUpperBound()),
+                             getIntegerAttrOrNull(node->getStride()));
+}
+
+DISubroutineTypeAttr
+DebugImporter::translateImpl(llvm::DISubroutineType *node) {
+  // Separate the result type since it is null for void functions.
+  DITypeAttr resultType = translate(*node->getTypeArray().begin());
+  SmallVector<DITypeAttr> argumentTypes;
+  for (llvm::DIType *type : llvm::drop_begin(node->getTypeArray())) {
+    assert(type && "expected a non-null argument type");
+    argumentTypes.push_back(translate(type));
+  }
+  return DISubroutineTypeAttr::get(context, node->getCC(), resultType,
+                                   argumentTypes);
+}
+
+DITypeAttr DebugImporter::translateImpl(llvm::DIType *node) {
+  return cast<DITypeAttr>(translate(static_cast<llvm::DINode *>(node)));
+}
+
+DINodeAttr DebugImporter::translate(llvm::DINode *node) {
+  if (!node)
+    return nullptr;
+
+  // Check for a cached instance.
+  if (DINodeAttr attr = nodeToAttr.lookup(node))
+    return attr;
+
+  // Convert the debug metadata if possible.
+  auto translateNode = [this](llvm::DINode *node) -> DINodeAttr {
+    if (auto *casted = dyn_cast<llvm::DIBasicType>(node))
+      return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DICompileUnit>(node))
+      return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DICompositeType>(node))
+      return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DIDerivedType>(node))
+      return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DIFile>(node))
+      return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DILexicalBlock>(node))
+      return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DILexicalBlockFile>(node))
+      return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DILocalVariable>(node))
+      return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DISubprogram>(node))
+      return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DISubrange>(node))
+      return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DISubroutineType>(node))
+      return translateImpl(casted);
+    return nullptr;
+  };
+  if (DINodeAttr attr = translateNode(node)) {
+    nodeToAttr.insert({node, attr});
+    return attr;
+  }
+  return nullptr;
+}
+
+//===----------------------------------------------------------------------===//
+// Locations
+//===----------------------------------------------------------------------===//
+
+Location DebugImporter::translateLoc(llvm::DILocation *loc) {
+  if (!loc)
+    return UnknownLoc::get(context);
+
+  // Get the file location of the instruction.
+  Location result = FileLineColLoc::get(context, loc->getFilename(),
+                                        loc->getLine(), loc->getColumn());
+
+  // Add call site information, if available.
+  if (llvm::DILocation *inlinedAt = loc->getInlinedAt())
+    result = CallSiteLoc::get(result, translateLoc(inlinedAt));
+
+  // Add scope information.
+  assert(loc->getScope() && "expected non-null scope");
+  result = FusedLocWith<DIScopeAttr>::get({result}, translate(loc->getScope()),
+                                          context);
+  return result;
+}
diff --git a/mlir/lib/Target/LLVMIR/DebugImporter.h b/mlir/lib/Target/LLVMIR/DebugImporter.h
new file mode 100644 (file)
index 0000000..4be4aa1
--- /dev/null
@@ -0,0 +1,78 @@
+//===- DebugImporter.h - LLVM to MLIR Debug conversion -------*- C++ -*----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the translation between LLVMIR debug information and
+// the corresponding MLIR representation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_LIB_TARGET_LLVMIR_DEBUGIMPORT_H_
+#define MLIR_LIB_TARGET_LLVMIR_DEBUGIMPORT_H_
+
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/MLIRContext.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+
+namespace mlir {
+class Operation;
+
+namespace LLVM {
+class LLVMFuncOp;
+
+namespace detail {
+
+class DebugImporter {
+public:
+  DebugImporter(MLIRContext *context) : context(context) {}
+
+  /// Translates the given LLVM debug location to an MLIR location.
+  Location translateLoc(llvm::DILocation *loc);
+
+  /// Translates the debug information for the given function.
+  void translate(llvm::Function *func, LLVMFuncOp funcOp);
+
+  /// Translates the given LLVM debug metadata to MLIR.
+  DINodeAttr translate(llvm::DINode *node);
+
+  /// Infers the metadata type and translates it to MLIR.
+  template <typename DINodeT>
+  auto translate(DINodeT *node) {
+    // Infer the MLIR type from the LLVM metadata type.
+    using MLIRTypeT = decltype(translateImpl(node));
+    return cast_or_null<MLIRTypeT>(
+        translate(static_cast<llvm::DINode *>(node)));
+  }
+
+private:
+  /// Translates the given LLVM debug metadata to the corresponding attribute.
+  DIBasicTypeAttr translateImpl(llvm::DIBasicType *node);
+  DICompileUnitAttr translateImpl(llvm::DICompileUnit *node);
+  DICompositeTypeAttr translateImpl(llvm::DICompositeType *node);
+  DIDerivedTypeAttr translateImpl(llvm::DIDerivedType *node);
+  DIFileAttr translateImpl(llvm::DIFile *node);
+  DILexicalBlockAttr translateImpl(llvm::DILexicalBlock *node);
+  DILexicalBlockFileAttr translateImpl(llvm::DILexicalBlockFile *node);
+  DILocalVariableAttr translateImpl(llvm::DILocalVariable *node);
+  DIScopeAttr translateImpl(llvm::DIScope *node);
+  DISubprogramAttr translateImpl(llvm::DISubprogram *node);
+  DISubrangeAttr translateImpl(llvm::DISubrange *node);
+  DISubroutineTypeAttr translateImpl(llvm::DISubroutineType *node);
+  DITypeAttr translateImpl(llvm::DIType *node);
+
+  /// A mapping between LLVM debug metadata and the corresponding attribute.
+  DenseMap<llvm::DINode *, DINodeAttr> nodeToAttr;
+
+  MLIRContext *context;
+};
+
+} // namespace detail
+} // namespace LLVM
+} // namespace mlir
+
+#endif // MLIR_LIB_TARGET_LLVMIR_DEBUIMPORTN_H_
index 31f3f82..575ac01 100644 (file)
@@ -17,26 +17,212 @@ next:
 
 ; // -----
 
-; CHECK-LABEL: @known_loc(
-define i32 @known_loc(i32 %0) {
-entry:
-  br label %next
-end:
-  ; CHECK: ^{{.*}}(%{{.+}}: i32 loc("known_loc.cpp":5:2)):
-  %1 = phi i32 [ %2, %next ], !dbg !4
-  ret i32 %1
-next:
-  ; CHECK: = llvm.mul %{{.+}}, %{{.+}} : i32 loc(#[[LOC:.+]])
-  %2 = mul i32 %0, %0, !dbg !5
-  br label %end
+; CHECK: #[[$SP:.+]] =  #llvm.di_subprogram<compileUnit = #{{.*}}, scope = #{{.*}}, name = "instruction_loc"
+; CHECK: #[[$CALLEE:.+]] =  #llvm.di_subprogram<compileUnit = #{{.*}}, scope = #{{.*}}, name = "callee"
+
+; CHECK-LABEL: @instruction_loc
+define i32 @instruction_loc(i32 %arg1) {
+  ; CHECK llvm.add {{.*}} loc(#[[FILE_LOC:.*]])
+  %1 = add i32 %arg1, %arg1, !dbg !5
+
+  ; CHECK llvm.mul {{.*}} loc(#[[CALLSITE_LOC:.*]])
+  %2 = mul i32 %1, %1, !dbg !7
+
+  ret i32 %2
+}
+; CHECK #[[FILE_LOC]] = loc(fused<#[[$SP]]>["debug-info.ll":1:2])
+; CHECK #[[CALLSITE_LOC]] = loc(fused<#[[$CALLEE]]>[callsite("debug-info.ll":7:4 at fused<#[[$SP]]>["debug-info.ll":2:2])])
+
+!llvm.dbg.cu = !{!1}
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C, file: !2)
+!2 = !DIFile(filename: "debug-info.ll", directory: "/")
+!3 = distinct !DISubprogram(name: "instruction_loc", scope: !2, file: !2, line: 42, scopeLine: 42, spFlags: DISPFlagDefinition, unit: !1)
+!4 = distinct !DISubprogram(name: "callee", scope: !2, file: !2, line: 43, scopeLine: 42, spFlags: DISPFlagDefinition, unit: !1)
+!5 = !DILocation(line: 1, column: 2, scope: !3)
+!6 = !DILocation(line: 2, column: 2, scope: !3)
+!7 = !DILocation(line: 7, column: 4, scope: !4, inlinedAt: !6)
+
+; // -----
+
+; CHECK: #[[FILE:.+]] = #llvm.di_file<"debug-info.ll" in "/">
+; CHECK: #[[SP:.+]] = #llvm.di_subprogram<compileUnit =
+; CHECK: #[[$LB0:.+]] = #llvm.di_lexical_block<scope = #[[SP]], line = 0, column = 0>
+; CHECK: #[[$LB1:.+]] = #llvm.di_lexical_block<scope = #[[SP]], file = #[[FILE]], line = 2, column = 2>
+
+; CHECK-LABEL: @lexical_block
+define i32 @lexical_block(i32 %arg1) {
+  ; CHECK llvm.add {{.*}} loc(#[[LOC0:.*]])
+  %1 = add i32 %arg1, %arg1, !dbg !6
+
+  ; CHECK llvm.mul {{.*}} loc(#[[LOC1:.*]])
+  %2 = mul i32 %arg1, %arg1, !dbg !7
+
+  ret i32 %2
+}
+; CHECK #[[LOC0]] = loc(fused<#[[$LB0]]>["debug-info.ll":1:2])
+; CHECK #[[LOC1]] = loc(fused<#[[$LB1]]>["debug-info.ll":1:2])
+
+!llvm.dbg.cu = !{!1}
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C, file: !2)
+!2 = !DIFile(filename: "debug-info.ll", directory: "/")
+!3 = distinct !DISubprogram(name: "lexical_block", scope: !2, file: !2, line: 42, scopeLine: 42, spFlags: DISPFlagDefinition, unit: !1)
+!4 = !DILexicalBlock(scope: !3)
+!5 = !DILexicalBlock(scope: !3, file: !2, line: 2, column: 2)
+!6 = !DILocation(line: 1, column: 2, scope: !4)
+!7 = !DILocation(line: 2, column: 2, scope: !5)
+
+; // -----
+
+; CHECK: #[[FILE:.+]] = #llvm.di_file<"debug-info.ll" in "/">
+; CHECK: #[[SP:.+]] = #llvm.di_subprogram<compileUnit =
+; CHECK: #[[$LB0:.+]] = #llvm.di_lexical_block_file<scope = #[[SP]], discriminator = 0>
+; CHECK: #[[$LB1:.+]] = #llvm.di_lexical_block_file<scope = #[[SP]], file = #[[FILE]], discriminator = 0>
+
+; CHECK-LABEL: @lexical_block_file
+define i32 @lexical_block_file(i32 %arg1) {
+  ; CHECK llvm.add {{.*}} loc(#[[LOC0:.*]])
+  %1 = add i32 %arg1, %arg1, !dbg !6
+
+  ; CHECK llvm.mul {{.*}} loc(#[[LOC1:.*]])
+  %2 = mul i32 %arg1, %arg1, !dbg !7
+
+  ret i32 %2
+}
+; CHECK #[[LOC0]] = loc(fused<#[[$LB0]]>["debug-info.ll":1:2]))
+; CHECK #[[LOC1]] = loc(fused<#[[$LB1]]>["debug-info.ll":2:2]))
+
+!llvm.dbg.cu = !{!1}
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C, file: !2)
+!2 = !DIFile(filename: "debug-info.ll", directory: "/")
+!3 = distinct !DISubprogram(name: "lexical_block_file", scope: !2, file: !2, line: 42, scopeLine: 42, spFlags: DISPFlagDefinition, unit: !1)
+!4 = !DILexicalBlockFile(scope: !3, discriminator: 0)
+!5 = !DILexicalBlockFile(scope: !3, file: !2, discriminator: 0)
+!6 = !DILocation(line: 1, column: 2, scope: !4)
+!7 = !DILocation(line: 2, column: 2, scope: !5)
+
+; // -----
+
+; CHECK: #[[INT1:.+]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "int1">
+; CHECK: #[[INT2:.+]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "int2", sizeInBits = 32, encoding = DW_ATE_signed>
+; CHECK: #llvm.di_subroutine_type<argumentTypes = #[[INT1]], #[[INT2]]>
+
+define void @basic_type() !dbg !3 {
+  ret void
+}
+
+!llvm.dbg.cu = !{!1}
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C, file: !2)
+!2 = !DIFile(filename: "debug-info.ll", directory: "/")
+!3 = distinct !DISubprogram(name: "basic_type", scope: !2, file: !2, line: 42, scopeLine: 42, spFlags: DISPFlagDefinition, unit: !1, type: !4)
+!4 = !DISubroutineType(types: !5)
+!5 = !{null, !6, !7}
+!6 = !DIBasicType(name: "int1")
+!7 = !DIBasicType(name: "int2", encoding: DW_ATE_signed, size: 32)
+
+; // -----
+
+; CHECK: #[[INT:.+]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "int">
+; CHECK: #[[PTR1:.+]] = #llvm.di_derived_type<tag = DW_TAG_pointer_type, baseType = #[[INT]], sizeInBits = 0, alignInBits = 0, offsetInBits = 0>
+; CHECK: #[[PTR2:.+]] = #llvm.di_derived_type<tag = DW_TAG_pointer_type, name = "mypointer", baseType = #[[INT]], sizeInBits = 64, alignInBits = 32, offsetInBits = 4>
+; CHECK: #llvm.di_subroutine_type<argumentTypes = #[[PTR1]], #[[PTR2]]>
+
+define void @derived_type() !dbg !3 {
+  ret void
 }
-; CHECK: #[[LOC]] = loc("known_loc.cpp":8:3)
 
-!llvm.dbg.cu = !{!0}
-!llvm.module.flags = !{!1}
-!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !2)
-!1 = !{i32 2, !"Debug Info Version", i32 3}
-!2 = !DIFile(filename: "known_loc.cpp", directory: "/")
-!3 = distinct !DISubprogram(name: "known_loc", scope: !0, file: !2, line: 1, scopeLine: 1, unit: !0)
-!4 = !DILocation(line: 5, column: 2, scope: !3)
-!5 = !DILocation(line: 8, column: 3, scope: !3)
+!llvm.dbg.cu = !{!1}
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C, file: !2)
+!2 = !DIFile(filename: "debug-info.ll", directory: "/")
+!3 = distinct !DISubprogram(name: "derived_type", scope: !2, file: !2, line: 42, scopeLine: 42, spFlags: DISPFlagDefinition, unit: !1, type: !4)
+!4 = !DISubroutineType(types: !5)
+!5 = !{null, !7, !8}
+!6 = !DIBasicType(name: "int")
+!7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6)
+!8 = !DIDerivedType(name: "mypointer", tag: DW_TAG_pointer_type, baseType: !6, size: 64, align: 32, offset: 4)
+
+; // -----
+
+; CHECK: #[[INT:.+]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "int">
+; CHECK: #[[FILE:.+]] = #llvm.di_file<"debug-info.ll" in "/">
+; CHECK: #[[COMP1:.+]] = #llvm.di_composite_type<tag = DW_TAG_array_type, name = "array1", line = 10, sizeInBits = 128, alignInBits = 32>
+; CHECK: #[[COMP2:.+]] = #llvm.di_composite_type<{{.*}}, file = #[[FILE]], line = 0, scope = #[[FILE]], baseType = #[[INT]], sizeInBits = 0, alignInBits = 0>
+; CHECK: #[[COMP3:.+]] = #llvm.di_composite_type<{{.*}}, flags = Vector, {{.*}}, elements = #llvm.di_subrange<count = 4 : i64>>
+; CHECK: #[[COMP4:.+]] = #llvm.di_composite_type<{{.*}}, elements = #llvm.di_subrange<lowerBound = 0 : i64, upperBound = 4 : i64, stride = 1 : i64>>
+; CHECK: #llvm.di_subroutine_type<argumentTypes = #[[COMP1]], #[[COMP2]], #[[COMP3]], #[[COMP4]]>
+
+define void @composite_type() !dbg !3 {
+  ret void
+}
+
+!llvm.dbg.cu = !{!1}
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C, file: !2)
+!2 = !DIFile(filename: "debug-info.ll", directory: "/")
+!3 = distinct !DISubprogram(name: "composite_type", scope: !2, file: !2, line: 42, scopeLine: 42, spFlags: DISPFlagDefinition, unit: !1, type: !4)
+!4 = !DISubroutineType(types: !5)
+!5 = !{null, !7, !8, !9, !10}
+!6 = !DIBasicType(name: "int")
+!7 = !DICompositeType(tag: DW_TAG_array_type, name: "array1", line: 10, size: 128, align: 32)
+!8 = !DICompositeType(tag: DW_TAG_array_type, name: "array2", file: !2, scope: !2, baseType: !6)
+!9 = !DICompositeType(tag: DW_TAG_array_type, name: "array3", flags: DIFlagVector, elements: !13)
+!10 = !DICompositeType(tag: DW_TAG_array_type, name: "array4", flags: DIFlagVector, elements: !14)
+!11 = !DISubrange(count: 4)
+!12 = !DISubrange(lowerBound: 0, upperBound: 4, stride: 1)
+!13 = !{!11}
+!14 = !{!12}
+
+; // -----
+
+; CHECK: #[[INT:.+]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "int">
+; CHECK: #[[FILE:.+]] = #llvm.di_file<"debug-info.ll" in "/">
+; CHECK: #[[CU:.+]] = #llvm.di_compile_unit<sourceLanguage = DW_LANG_C, file = #[[FILE]], producer = "", isOptimized = false, emissionKind = None>
+; CHECK: #[[SP_TYPE:.+]] = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, resultType = #[[INT]], argumentTypes = #[[INT]]>
+; CHECK: #[[SP:.+]] = #llvm.di_subprogram<compileUnit = #[[CU]], scope = #[[FILE]], name = "subprogram", linkageName = "subprogram", file = #[[FILE]], line = 42, scopeLine = 42, subprogramFlags = Definition, type = #[[SP_TYPE]]>
+
+define void @subprogram() !dbg !3 {
+  ret void
+}
+
+!llvm.dbg.cu = !{!1}
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C, file: !2)
+!2 = !DIFile(filename: "debug-info.ll", directory: "/")
+!3 = distinct !DISubprogram(name: "subprogram", linkageName: "subprogram", scope: !2, file: !2, line: 42, scopeLine: 42, spFlags: DISPFlagDefinition, unit: !1, type: !4)
+!4 = !DISubroutineType(cc: DW_CC_normal, types: !5)
+!5 = !{!6, !6}
+!6 = !DIBasicType(name: "int")
+
+; // -----
+
+; CHECK: #[[$SP:.+]] =  #llvm.di_subprogram<compileUnit = #{{.*}}, scope = #{{.*}}, name = "func_loc", file = #{{.*}}, line = 42, scopeLine = 42, subprogramFlags = Definition>
+
+; CHECK-LABEL: @func_loc
+define void @func_loc() !dbg !3 {
+  ret void
+}
+; CHECK: loc(fused<#[[$SP]]>["func_loc"])
+
+!llvm.dbg.cu = !{!1}
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C, file: !2)
+!2 = !DIFile(filename: "debug-info.ll", directory: "/")
+!3 = distinct !DISubprogram(name: "func_loc", scope: !2, file: !2, line: 42, scopeLine: 42, spFlags: DISPFlagDefinition, unit: !1)
+
+; // -----
+
+; Verify the module location is set to the source filename.
+; CHECK: loc("debug-info.ll":0:0)
+source_filename = "debug-info.ll"