[mlir][llvm] Make some debug info attribute parameters optional.
authorTobias Gysi <tobias.gysi@nextsilicon.com>
Fri, 18 Nov 2022 08:35:15 +0000 (09:35 +0100)
committerTobias Gysi <tobias.gysi@nextsilicon.com>
Fri, 18 Nov 2022 08:35:44 +0000 (09:35 +0100)
The revision makes specific debug information attribute parameters
optional since some of them can be omitted in LLVMIR. The additional
flexibility enables a later revision that will support importing
debug information from LLVMIR. A special case is the types parameter
of the SubroutineTypeAttr. For void functions, its first entry is
null in LLVMIR. This revision splits the type parameter in an optional
resultType parameter and an argumentTypes array to support this corner
case.

Reviewed By: rriddle

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

mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
mlir/lib/Target/LLVMIR/DebugTranslation.cpp
mlir/test/Dialect/LLVMIR/debuginfo.mlir
mlir/test/Target/LLVMIR/llvmir-debug.mlir

index c8634db..6335cbc 100644 (file)
@@ -88,11 +88,11 @@ def LoopOptionsAttr : LLVM_Attr<"LoopOptions", "loopopts"> {
 // DebugInfo Attributes
 //===----------------------------------------------------------------------===//
 
-class LLVM_DIParameter<string summary, string underlyingType, string parseName,
+class LLVM_DIParameter<string summary, string default, string parseName,
                        string printName = parseName>
-    : AttrOrTypeParameter<underlyingType, "debug info " # summary> {
+    : AttrOrTypeParameter<"unsigned", "debug info " # summary> {
   let parser = [{ [&]() -> FailureOr<unsigned> {
-    SMLoc tagLoc = $_parser.getCurrentLocation(); 
+    SMLoc tagLoc = $_parser.getCurrentLocation();
     StringRef name;
     if ($_parser.parseKeyword(&name))
       return failure();
@@ -103,22 +103,23 @@ class LLVM_DIParameter<string summary, string underlyingType, string parseName,
       << "invalid debug info }] # summary # [{ name: " << name;
   }() }];
   let printer = "$_printer << llvm::dwarf::" # printName # "String($_self)";
+  let defaultValue = default;
 }
 
 def LLVM_DICallingConventionParameter : LLVM_DIParameter<
-  "calling convention", "unsigned", "CallingConvention", "Convention" 
+  "calling convention", /*default=*/"0", "CallingConvention", "Convention"
 >;
 
 def LLVM_DIEncodingParameter : LLVM_DIParameter<
-  "encoding", "unsigned", "AttributeEncoding"
+  "encoding", /*default=*/"0", "AttributeEncoding"
 >;
 
 def LLVM_DILanguageParameter : LLVM_DIParameter<
-  "language", "unsigned", "Language"
+  "language", /*default=*/"", "Language"
 >;
 
 def LLVM_DITagParameter : LLVM_DIParameter<
-  "tag", "unsigned", "Tag"
+  "tag", /*default=*/"", "Tag"
 >;
 
 //===----------------------------------------------------------------------===//
@@ -130,7 +131,7 @@ def LLVM_DIBasicTypeAttr : LLVM_Attr<"DIBasicType", "di_basic_type",
   let parameters = (ins
     LLVM_DITagParameter:$tag,
     "StringAttr":$name,
-    "uint64_t":$sizeInBits,
+    OptionalParameter<"uint64_t">:$sizeInBits,
     LLVM_DIEncodingParameter:$encoding
   );
 
@@ -194,7 +195,7 @@ def LLVM_DIDerivedTypeAttr : LLVM_Attr<"DIDerivedType", "di_derived_type", [
   ], "DITypeAttr"> {
   let parameters = (ins
     LLVM_DITagParameter:$tag,
-    "StringAttr":$name,
+    OptionalParameter<"StringAttr">:$name,
     "DITypeAttr":$baseType,
     "uint64_t":$sizeInBits,
     "uint32_t":$alignInBits,
@@ -226,7 +227,7 @@ def LLVM_DILexicalBlockAttr : LLVM_Attr<"DILexicalBlock", "di_lexical_block", [
   ], "DIScopeAttr"> {
   let parameters = (ins
     "DIScopeAttr":$scope,
-    "DIFileAttr":$file,
+    OptionalParameter<"DIFileAttr">:$file,
     "unsigned":$line,
     "unsigned":$column
   );
@@ -250,14 +251,14 @@ def LLVM_DILexicalBlockFile : LLVM_Attr<"DILexicalBlockFile", "di_lexical_block_
   ], "DIScopeAttr"> {
   let parameters = (ins
     "DIScopeAttr":$scope,
-    "DIFileAttr":$file,
-    "unsigned":$descriminator
+    OptionalParameter<"DIFileAttr">:$file,
+    "unsigned":$discriminator
   );
   let builders = [
     AttrBuilderWithInferredContext<(ins
-      "DIScopeAttr":$scope, "DIFileAttr":$file, "unsigned":$descriminator
+      "DIScopeAttr":$scope, "DIFileAttr":$file, "unsigned":$discriminator
     ), [{
-      return $_get(file.getContext(), scope, file, descriminator);
+      return $_get(file.getContext(), scope, file, discriminator);
     }]>
   ];
   let assemblyFormat = "`<` struct(params) `>`";
@@ -304,18 +305,18 @@ def LLVM_DISubprogramAttr : LLVM_Attr<"DISubprogram", "di_subprogram", [
     "DICompileUnitAttr":$compileUnit,
     "DIScopeAttr":$scope,
     "StringAttr":$name,
-    "StringAttr":$linkageName,
+    OptionalParameter<"StringAttr">:$linkageName,
     "DIFileAttr":$file,
     "unsigned":$line,
     "unsigned":$scopeLine,
     "DISubprogramFlags":$subprogramFlags,
-    "DISubroutineTypeAttr":$type
+    OptionalParameter<"DISubroutineTypeAttr">:$type
   );
   let builders = [
     AttrBuilderWithInferredContext<(ins
       "DICompileUnitAttr":$compileUnit, "DIScopeAttr":$scope, "StringRef":$name,
-      "StringRef":$linkageName, "DIFileAttr":$file, "unsigned":$line, 
-      "unsigned":$scopeLine, "DISubprogramFlags":$subprogramFlags, 
+      "StringRef":$linkageName, "DIFileAttr":$file, "unsigned":$line,
+      "unsigned":$scopeLine, "DISubprogramFlags":$subprogramFlags,
       "DISubroutineTypeAttr":$type
     ), [{
       MLIRContext *ctx = file.getContext();
@@ -335,7 +336,7 @@ def LLVM_DISubprogramAttr : LLVM_Attr<"DISubprogram", "di_subprogram", [
 def LLVM_DISubrangeAttr : LLVM_Attr<"DISubrange", "di_subrange", /*traits=*/[],
                                     "DINodeAttr"> {
   let parameters = (ins
-    "IntegerAttr":$count,
+    OptionalParameter<"IntegerAttr">:$count,
     OptionalParameter<"IntegerAttr">:$lowerBound,
     OptionalParameter<"IntegerAttr">:$upperBound,
     OptionalParameter<"IntegerAttr">:$stride
@@ -352,11 +353,13 @@ def LLVM_DISubroutineTypeAttr : LLVM_Attr<"DISubroutineType", "di_subroutine_typ
   ], "DITypeAttr"> {
   let parameters = (ins
     LLVM_DICallingConventionParameter:$callingConvention,
-    OptionalArrayRefParameter<"DITypeAttr">:$types
+    OptionalParameter<"DITypeAttr">:$resultType,
+    OptionalArrayRefParameter<"DITypeAttr">:$argumentTypes
   );
   let builders = [
-    TypeBuilder<(ins "ArrayRef<DITypeAttr>":$types), [{
-      return $_get($_ctxt, /*callingConvention=*/0, types);
+    TypeBuilder<(ins "DITypeAttr":$resultType,
+                     "ArrayRef<DITypeAttr>":$argumentTypes), [{
+      return $_get($_ctxt, /*callingConvention=*/0, resultType, argumentTypes);
     }]>
   ];
   let assemblyFormat = "`<` struct(params) `>`";
index e851c27..4e3656b 100644 (file)
@@ -117,8 +117,12 @@ DebugTranslation::translateImpl(DICompositeTypeAttr attr) {
 }
 
 llvm::DIDerivedType *DebugTranslation::translateImpl(DIDerivedTypeAttr attr) {
+  auto getMDStringOrNull = [&](StringAttr attr) -> llvm::MDString * {
+    return attr ? llvm::MDString::get(llvmCtx, attr) : nullptr;
+  };
   return llvm::DIDerivedType::get(
-      llvmCtx, attr.getTag(), attr.getName(), /*File=*/nullptr, /*Line=*/0,
+      llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()),
+      /*File=*/nullptr, /*Line=*/0,
       /*Scope=*/nullptr, translate(attr.getBaseType()), attr.getSizeInBits(),
       attr.getAlignInBits(), attr.getOffsetInBits(),
       /*DWARFAddressSpace=*/llvm::None, /*Flags=*/llvm::DINode::FlagZero);
@@ -138,7 +142,7 @@ llvm::DILexicalBlockFile *
 DebugTranslation::translateImpl(DILexicalBlockFileAttr attr) {
   return llvm::DILexicalBlockFile::getDistinct(
       llvmCtx, translate(attr.getScope()), translate(attr.getFile()),
-      attr.getDescriminator());
+      attr.getDiscriminator());
 }
 
 llvm::DILocalVariable *
@@ -167,12 +171,15 @@ static llvm::DISubprogram *getSubprogram(bool isDistinct, Ts &&...args) {
 llvm::DISubprogram *DebugTranslation::translateImpl(DISubprogramAttr attr) {
   bool isDefinition = static_cast<bool>(attr.getSubprogramFlags() &
                                         LLVM::DISubprogramFlags::Definition);
+  auto getMDStringOrNull = [&](StringAttr attr) -> llvm::MDString * {
+    return attr ? llvm::MDString::get(llvmCtx, attr) : nullptr;
+  };
   return getSubprogram(
       isDefinition, llvmCtx, translate(attr.getScope()),
       llvm::MDString::get(llvmCtx, attr.getName()),
-      llvm::MDString::get(llvmCtx, attr.getLinkageName()),
-      translate(attr.getFile()), attr.getLine(), translate(attr.getType()),
-      attr.getScopeLine(), /*ContainingType=*/nullptr, /*VirtualIndex=*/0,
+      getMDStringOrNull(attr.getLinkageName()), translate(attr.getFile()),
+      attr.getLine(), translate(attr.getType()), attr.getScopeLine(),
+      /*ContainingType=*/nullptr, /*VirtualIndex=*/0,
       /*ThisAdjustment=*/0, llvm::DINode::FlagZero,
       static_cast<llvm::DISubprogram::DISPFlags>(attr.getSubprogramFlags()),
       translate(attr.getCompileUnit()));
@@ -193,8 +200,9 @@ llvm::DISubrange *DebugTranslation::translateImpl(DISubrangeAttr attr) {
 
 llvm::DISubroutineType *
 DebugTranslation::translateImpl(DISubroutineTypeAttr attr) {
-  SmallVector<llvm::Metadata *> types;
-  for (auto type : attr.getTypes())
+  // Concatenate the result and argument types into a single array.
+  SmallVector<llvm::Metadata *> types = {translate(attr.getResultType())};
+  for (DITypeAttr type : attr.getArgumentTypes())
     types.push_back(translate(type));
   return llvm::DISubroutineType::get(
       llvmCtx, llvm::DINode::FlagZero, attr.getCallingConvention(),
index 5e7401f..07f8fb3 100644 (file)
 // RUN: mlir-opt %s | mlir-opt | FileCheck %s
 
-// CHECK: #[[TYPE:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "si64", sizeInBits = 0, encoding = DW_ATE_signed>
-#si64 = #llvm.di_basic_type<
-  tag = DW_TAG_base_type, name = "si64", sizeInBits = 0,
-  encoding = DW_ATE_signed
->
-
-// CHECK: #[[FILE:.*]] = #llvm.di_file<"debuginfo.mlir" in "/test/">
+// CHECK-DAG: #[[FILE:.*]] = #llvm.di_file<"debuginfo.mlir" in "/test/">
 #file = #llvm.di_file<"debuginfo.mlir" in "/test/">
 
-// CHECK: #[[CU:.*]] = #llvm.di_compile_unit<sourceLanguage = DW_LANG_C, file = #[[FILE]], producer = "MLIR", isOptimized = true, emissionKind = Full>
+// CHECK-DAG: #[[CU:.*]] = #llvm.di_compile_unit<sourceLanguage = DW_LANG_C, file = #[[FILE]], producer = "MLIR", isOptimized = true, emissionKind = Full>
 #cu = #llvm.di_compile_unit<
   sourceLanguage = DW_LANG_C, file = #file, producer = "MLIR",
   isOptimized = true, emissionKind = Full
 >
 
-// CHECK: #[[SPTYPE:.*]] = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, types = #[[TYPE]]>
-#spType = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, types = #si64>
+// CHECK-DAG: #[[INT0:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "int0">
+#int0 = #llvm.di_basic_type<
+  // Omit the optional sizeInBits and encoding parameters.
+  tag = DW_TAG_base_type, name = "int0"
+>
+
+// CHECK-DAG: #[[INT1:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "int1", sizeInBits = 32, encoding = DW_ATE_signed>
+#int1 = #llvm.di_basic_type<
+  tag = DW_TAG_base_type, name = "int1",
+  sizeInBits = 32, encoding = DW_ATE_signed
+>
+
+// CHECK-DAG: #[[PTR0:.*]] = #llvm.di_derived_type<tag = DW_TAG_pointer_type, baseType = #[[INT0]], sizeInBits = 0, alignInBits = 0, offsetInBits = 0>
+#ptr0 = #llvm.di_derived_type<
+  tag = DW_TAG_pointer_type, baseType = #int0,
+  sizeInBits = 0, alignInBits = 0, offsetInBits = 0
+>
+
+// CHECK-DAG: #[[PTR1:.*]] = #llvm.di_derived_type<tag = DW_TAG_pointer_type, name = "ptr1", baseType = #[[INT0]], sizeInBits = 64, alignInBits = 32, offsetInBits = 4>
+#ptr1 = #llvm.di_derived_type<
+  // Specify the name parameter.
+  tag = DW_TAG_pointer_type, name = "ptr1", baseType = #int0,
+  sizeInBits = 64, alignInBits = 32, offsetInBits = 4
+>
+
+// CHECK-DAG: #[[COMP0:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type, name = "array0", line = 10, sizeInBits = 128, alignInBits = 32>
+#comp0 = #llvm.di_composite_type<
+  // Omit optional parameters.
+  tag = DW_TAG_array_type, name = "array0",
+  line = 10, sizeInBits = 128, alignInBits = 32
+>
+
+// CHECK-DAG: #[[COMP1:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type, name = "array1", file = #[[FILE]], line = 0, scope = #[[FILE]], baseType = #[[INT0]], sizeInBits = 0, alignInBits = 0, elements = #llvm.di_subrange<count = 4 : i64>>
+#comp1 = #llvm.di_composite_type<
+  tag = DW_TAG_array_type, name = "array1", file = #file,
+  line = 0, scope = #file, baseType = #int0, sizeInBits = 0, alignInBits = 0,
+  // Specify the subrange count.
+  elements = #llvm.di_subrange<count = 4>
+>
+
+// CHECK-DAG: #[[COMP2:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type, name = "array2", file = #[[FILE]], line = 0, scope = #[[FILE]], baseType = #[[INT0]], sizeInBits = 0, alignInBits = 0, elements = #llvm.di_subrange<lowerBound = 0 : i64, upperBound = 4 : i64, stride = 1 : i64>>
+#comp2 = #llvm.di_composite_type<
+  tag = DW_TAG_array_type, name = "array2", file = #file,
+  line = 0, scope = #file, baseType = #int0, sizeInBits = 0, alignInBits = 0,
+  // Specify the subrange bounds.
+  elements = #llvm.di_subrange<lowerBound = 0, upperBound = 4, stride = 1>
+>
+
+// CHECK-DAG: #[[SPTYPE0:.*]] = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, argumentTypes = #[[INT0]], #[[PTR0]], #[[PTR1]], #[[COMP0:.*]], #[[COMP1:.*]], #[[COMP2:.*]]>
+#spType0 = #llvm.di_subroutine_type<
+  callingConvention = DW_CC_normal, argumentTypes = #int0, #ptr0, #ptr1, #comp0, #comp1, #comp2
+>
 
-// CHECK: #[[SP:.*]] = #llvm.di_subprogram<compileUnit = #[[CU]], scope = #[[FILE]], name = "intrinsics", linkageName = "intrinsics", file = #[[FILE]], line = 3, scopeLine = 3, subprogramFlags = "Definition|Optimized", type = #[[SPTYPE]]>
-#sp = #llvm.di_subprogram<
-  compileUnit = #cu, scope = #file, name = "intrinsics", linkageName = "intrinsics",
-  file = #file, line = 3, scopeLine = 3, subprogramFlags = "Definition|Optimized", type = #spType
+// CHECK-DAG: #[[SPTYPE1:.*]] = #llvm.di_subroutine_type<resultType = #[[INT1]], argumentTypes = #[[INT1]]>
+#spType1 = #llvm.di_subroutine_type<
+  // Omit the optional callingConvention parameter.
+  resultType = #int1, argumentTypes = #int1
 >
 
-// CHECK: #[[VAR:.*]] = #llvm.di_local_variable<scope = #[[SP]], name = "arg", file = #[[FILE]], line = 6, arg = 1, alignInBits = 0, type = #[[TYPE]]>
-#variable = #llvm.di_local_variable<scope = #sp, name = "arg", file = #file, line = 6, arg = 1, alignInBits = 0, type = #si64>
+// CHECK-DAG: #[[SP0:.*]] = #llvm.di_subprogram<compileUnit = #[[CU]], scope = #[[FILE]], name = "addr", linkageName = "addr", file = #[[FILE]], line = 3, scopeLine = 3, subprogramFlags = "Definition|Optimized", type = #[[SPTYPE0]]>
+#sp0 = #llvm.di_subprogram<
+  compileUnit = #cu, scope = #file, name = "addr", linkageName = "addr",
+  file = #file, line = 3, scopeLine = 3, subprogramFlags = "Definition|Optimized", type = #spType0
+>
+
+// CHECK-DAG: #[[SP1:.*]] = #llvm.di_subprogram<compileUnit = #[[CU]], scope = #[[FILE]], name = "value", file = #[[FILE]], line = 4, scopeLine = 4, subprogramFlags = Definition, type = #[[SPTYPE1]]>
+#sp1 = #llvm.di_subprogram<
+  // Omit the optional linkageName parameter.
+  compileUnit = #cu, scope = #file, name = "value",
+  file = #file, line = 4, scopeLine = 4, subprogramFlags = "Definition", type = #spType1
+>
 
-// CHECK: llvm.func @intrinsics(%[[ARG:.*]]: i64)
-llvm.func @intrinsics(%arg: i64) {
-  // CHECK: %[[ALLOC:.*]] = llvm.alloca 
+// CHECK-DAG: #[[VAR0:.*]] = #llvm.di_local_variable<scope = #[[SP0]], name = "arg", file = #[[FILE]], line = 6, arg = 1, alignInBits = 0, type = #[[INT0]]>
+#var0 = #llvm.di_local_variable<
+  scope = #sp0, name = "arg", file = #file,
+  line = 6, arg = 1, alignInBits = 0, type = #int0
+>
+
+// CHECK-DAG: #[[VAR1:.*]] = #llvm.di_local_variable<scope = #[[SP1]], name = "arg", file = #[[FILE]], line = 7, arg = 2, alignInBits = 0, type = #[[INT1]]>
+#var1 = #llvm.di_local_variable<
+  scope = #sp1, name = "arg", file = #file,
+  line = 7, arg = 2, alignInBits = 0, type = #int1
+>
+
+// CHECK: llvm.func @addr(%[[ARG:.*]]: i64)
+llvm.func @addr(%arg: i64) {
+  // CHECK: %[[ALLOC:.*]] = llvm.alloca
   %allocCount = llvm.mlir.constant(1 : i32) : i32
   %alloc = llvm.alloca %allocCount x i64 : (i32) -> !llvm.ptr<i64>
 
-  // CHECK: llvm.dbg.value #[[VAR]] = %[[ARG]]
-  // CHECK: llvm.dbg.addr #[[VAR]] = %[[ALLOC]]
-  // CHECK: llvm.dbg.declare #[[VAR]] = %[[ALLOC]]
-  llvm.dbg.value #variable = %arg : i64
-  llvm.dbg.addr #variable = %alloc : !llvm.ptr<i64>
-  llvm.dbg.declare #variable = %alloc : !llvm.ptr<i64>
+  // CHECK: llvm.dbg.addr #[[VAR0]] = %[[ALLOC]]
+  // CHECK: llvm.dbg.declare #[[VAR0]] = %[[ALLOC]]
+  llvm.dbg.addr #var0 = %alloc : !llvm.ptr<i64>
+  llvm.dbg.declare #var0 = %alloc : !llvm.ptr<i64>
   llvm.return
 }
+
+// CHECK: llvm.func @value(%[[ARG:.*]]: i32)
+llvm.func @value(%arg: i32) -> i32 {
+  // CHECK: llvm.dbg.value #[[VAR1]] = %[[ARG]]
+  llvm.dbg.value #var1 = %arg : i32
+  llvm.return %arg : i32
+}
index 692e3fe..5026af9 100644 (file)
@@ -17,12 +17,24 @@ llvm.func @func_no_debug() {
   llvm.return loc(unknown)
 } loc(unknown)
 
-
+#file = #llvm.di_file<"foo.mlir" in "/test/">
 #si64 = #llvm.di_basic_type<
-  tag = DW_TAG_base_type, name = "si64", sizeInBits = 0,
-  encoding = DW_ATE_signed
+  // Omit the optional sizeInBits and encoding parameters.
+  tag = DW_TAG_base_type, name = "si64"
+>
+#si32 = #llvm.di_basic_type<
+  tag = DW_TAG_base_type, name = "si32",
+  sizeInBits = 32, encoding = DW_ATE_signed
+>
+#ptr = #llvm.di_derived_type<
+  tag = DW_TAG_pointer_type, baseType = #si32,
+  sizeInBits = 64, alignInBits = 0, offsetInBits = 0
+>
+#named = #llvm.di_derived_type<
+  // Specify the name parameter.
+  tag = DW_TAG_pointer_type, name = "named", baseType = #si32,
+  sizeInBits = 64, alignInBits = 0, offsetInBits = 0
 >
-#file = #llvm.di_file<"foo.mlir" in "/test/">
 #cu = #llvm.di_compile_unit<
   sourceLanguage = DW_LANG_C, file = #file, producer = "MLIR",
   isOptimized = true, emissionKind = Full
@@ -34,16 +46,23 @@ llvm.func @func_no_debug() {
 >
 #vector = #llvm.di_composite_type<
   tag = DW_TAG_array_type, name = "array", file = #file,
-  line = 0, baseType = #si64, sizeInBits = 0, alignInBits = 0,
-  flags = Vector,
-  elements = #llvm.di_subrange<count = 4>
+  line = 0, baseType = #si64, sizeInBits = 0, alignInBits = 0, flags = Vector,
+  elements = #llvm.di_subrange<lowerBound = 0, upperBound = 4, stride = 1>
 >
-#spType = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, types = #si64, #composite, #vector>
+#spType = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, argumentTypes = #si64, #ptr, #named, #composite, #vector>
 #sp = #llvm.di_subprogram<
-  compileUnit = #cu, scope = #file, name = "intrinsics", linkageName = "intrinsics",
+  compileUnit = #cu, scope = #file, name = "func_with_debug", linkageName = "func_with_debug",
   file = #file, line = 3, scopeLine = 3, subprogramFlags = "Definition|Optimized", type = #spType
 >
-#fileScope = #llvm.di_lexical_block_file<scope = #sp, file = #file, descriminator = 0>
+#calleeType = #llvm.di_subroutine_type<
+  // Omit the optional callingConvention parameter but specify a result type.
+  resultType = #si64, argumentTypes = #si64>
+#callee = #llvm.di_subprogram<
+  // Omit the linkageName parameter.
+  compileUnit = #cu, scope = #file, name = "callee",
+  file = #file, line = 4, scopeLine = 4, subprogramFlags = "Definition", type = #calleeType
+>
+#fileScope = #llvm.di_lexical_block_file<scope = #sp, file = #file, discriminator = 0>
 #variable = #llvm.di_local_variable<scope = #fileScope, name = "arg", file = #file, line = 6, arg = 1, alignInBits = 0, type = #si64>
 
 // CHECK-LABEL: define void @func_with_debug(
@@ -72,20 +91,29 @@ llvm.func @func_with_debug(%arg: i64) {
   // CHECK: call void @func_no_debug(), !dbg ![[FUSED_LOC:[0-9]+]]
   llvm.call @func_no_debug() : () -> () loc(fused[callsite("mysource.cc":5:6 at "mysource.cc":1:1), "mysource.cc":1:1])
 
+  // CHECK: add i64 %[[ARG]], %[[ARG]], !dbg ![[FUSEDWITH_LOC:[0-9]+]]
+  %sum = llvm.add %arg, %arg : i64 loc(fused<#callee>[callsite("foo.mlir":2:4 at fused<#sp>["foo.mlir":28:5])])
+
   llvm.return
 } loc(fused<#sp>["foo.mlir":1:1])
 
 // CHECK: ![[CU_LOC:.*]] = distinct !DICompileUnit(language: DW_LANG_C, file: ![[CU_FILE_LOC:.*]], producer: "MLIR", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
 // CHECK: ![[CU_FILE_LOC]] = !DIFile(filename: "foo.mlir", directory: "/test/")
 
-// CHECK: ![[FUNC_LOC]] = distinct !DISubprogram(name: "intrinsics", linkageName: "intrinsics", scope: ![[CU_FILE_LOC]], file: ![[CU_FILE_LOC]], line: 3, type: ![[FUNC_TYPE:.*]], scopeLine: 3, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: ![[CU_LOC]])
-// CHECK: ![[FUNC_TYPE]] = !DISubroutineType(cc: DW_CC_normal, types: ![[ARG_TYPES:.*]])
-// CHECK: ![[ARG_TYPES]] = !{![[ARG_TYPE:.*]], ![[COMPOSITE_TYPE:.*]], ![[VECTOR_TYPE:.*]]}
-// CHECK: ![[ARG_TYPE]] = !DIBasicType(name: "si64", encoding: DW_ATE_signed)
+// CHECK: ![[FUNC_LOC]] = distinct !DISubprogram(name: "func_with_debug", linkageName: "func_with_debug", scope: ![[CU_FILE_LOC]], file: ![[CU_FILE_LOC]], line: 3, type: ![[FUNC_TYPE:.*]], scopeLine: 3, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: ![[CU_LOC]])
+// CHECK: ![[FUNC_TYPE]] = !DISubroutineType(cc: DW_CC_normal, types: ![[FUNC_ARGS:.*]])
+// CHECK: ![[FUNC_ARGS]] = !{null, ![[ARG_TYPE:.*]], ![[PTR_TYPE:.*]], ![[NAMED_TYPE:.*]], ![[COMPOSITE_TYPE:.*]], ![[VECTOR_TYPE:.*]]}
+// CHECK: ![[ARG_TYPE]] = !DIBasicType(name: "si64")
+// CHECK: ![[PTR_TYPE]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[BASE_TYPE:.*]], size: 64)
+// CHECK: ![[BASE_TYPE]] = !DIBasicType(name: "si32", size: 32, encoding: DW_ATE_signed)
+// CHECK: ![[NAMED_TYPE]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "named", baseType: ![[BASE_TYPE:.*]], size: 64)
 // CHECK: ![[COMPOSITE_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "composite", file: ![[CU_FILE_LOC]], elements: ![[COMPOSITE_ELEMENTS:.*]])
 // CHECK: ![[COMPOSITE_ELEMENTS]] = !{![[COMPOSITE_ELEMENT:.*]]}
 // CHECK: ![[COMPOSITE_ELEMENT]] = !DISubrange(count: 4)
-// CHECK: ![[VECTOR_TYPE]] = !DICompositeType(tag: DW_TAG_array_type, name: "array", file: ![[CU_FILE_LOC]], baseType: ![[ARG_TYPE]], flags: DIFlagVector
+// CHECK: ![[VECTOR_TYPE]] = !DICompositeType(tag: DW_TAG_array_type, name: "array", file: ![[CU_FILE_LOC]], baseType: ![[ARG_TYPE]], flags: DIFlagVector, elements: ![[VECTOR_ELEMENTS:.*]])
+// CHECK: ![[VECTOR_ELEMENTS]] = !{![[VECTOR_ELEMENT:.*]]}
+// CHECK: ![[VECTOR_ELEMENT]] = !DISubrange(lowerBound: 0, upperBound: 4, stride: 1)
+
 // CHECK: ![[VAR_LOC]] = !DILocalVariable(name: "arg", arg: 1, scope: ![[VAR_SCOPE:.*]], file: ![[CU_FILE_LOC]], line: 6, type: ![[ARG_TYPE]])
 // CHECK: ![[VAR_SCOPE]] = distinct !DILexicalBlockFile(scope: ![[FUNC_LOC]], file: ![[CU_FILE_LOC]], discriminator: 0)
 
@@ -93,3 +121,10 @@ llvm.func @func_with_debug(%arg: i64) {
 // CHECK-DAG: ![[FILE_LOC]] = !DILocation(line: 1, column: 2,
 // CHECK-DAG: ![[NAMED_LOC]] = !DILocation(line: 10, column: 10
 // CHECK-DAG: ![[FUSED_LOC]] = !DILocation(line: 1, column: 1
+
+// CHECK: ![[FUSEDWITH_LOC]] = !DILocation(line: 2, column: 4, scope: ![[FUSEDWITH_SCOPE:.*]], inlinedAt: ![[INLINE_LOC:.*]])
+// CHECK: ![[FUSEDWITH_SCOPE]] = !DILexicalBlockFile(scope: ![[CALLEE_LOC:.*]], file:
+// CHECK: ![[CALLEE_LOC]] = distinct !DISubprogram(name: "callee", scope: ![[CU_FILE_LOC]], file: ![[CU_FILE_LOC]], line: 4, type: ![[CALLEE_TYPE:.*]], scopeLine: 4, spFlags: DISPFlagDefinition, unit: ![[CU_LOC]])
+// CHECK: ![[CALLEE_TYPE]] = !DISubroutineType(types: ![[CALLEE_ARGS:.*]])
+// CHECK: ![[CALLEE_ARGS]] = !{![[ARG_TYPE:.*]], ![[ARG_TYPE:.*]]}
+// CHECK: ![[INLINE_LOC]] = !DILocation(line: 28, column: 5,