[mlir][llvm] Add an explicit void type debug info attribute.
authorTobias Gysi <tobias.gysi@nextsilicon.com>
Thu, 12 Jan 2023 16:32:22 +0000 (17:32 +0100)
committerTobias Gysi <tobias.gysi@nextsilicon.com>
Thu, 12 Jan 2023 16:38:53 +0000 (17:38 +0100)
Previously, the DISubroutineType attribute used an optional result
parameter and an optional argument types array to model the subroutine
signature. LLVM IR debug metadata, on the other hand, has one types
list whose first entry maps to the result type. That entry may be
null to model a void result type. The type list may also be entirely
empty not specifying any type information. The latter is problematic
since the current DISubroutineType attribute cannot express it.

The revision changes DISubroutineTypeAttr to closely follow the
LLVM metadata design. In particular, it uses a single types parameter
array to model the subroutine signature and introduces an explicit
DIVoidResultTypeAttr to model the null entries.

Reviewed By: Dinistro

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

mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
mlir/lib/Target/LLVMIR/DebugImporter.cpp
mlir/lib/Target/LLVMIR/DebugTranslation.cpp
mlir/lib/Target/LLVMIR/DebugTranslation.h
mlir/test/Dialect/LLVMIR/debuginfo.mlir
mlir/test/Dialect/LLVMIR/invalid.mlir
mlir/test/Target/LLVMIR/Import/debug-info.ll
mlir/test/Target/LLVMIR/llvmir-debug.mlir

index f38558c..733d573 100644 (file)
@@ -123,6 +123,15 @@ def LLVM_DITagParameter : LLVM_DIParameter<
 >;
 
 //===----------------------------------------------------------------------===//
+// DIVoidResultTypeAttr
+//===----------------------------------------------------------------------===//
+
+def LLVM_DIVoidResultTypeAttr : LLVM_Attr<"DIVoidResultType", "di_void_result_type",
+                                          /*traits=*/[], "DITypeAttr"> {
+  let parameters = (ins);
+}
+
+//===----------------------------------------------------------------------===//
 // DIBasicTypeAttr
 //===----------------------------------------------------------------------===//
 
@@ -353,16 +362,15 @@ def LLVM_DISubroutineTypeAttr : LLVM_Attr<"DISubroutineType", "di_subroutine_typ
   ], "DITypeAttr"> {
   let parameters = (ins
     LLVM_DICallingConventionParameter:$callingConvention,
-    OptionalParameter<"DITypeAttr">:$resultType,
-    OptionalArrayRefParameter<"DITypeAttr">:$argumentTypes
+    OptionalArrayRefParameter<"DITypeAttr">:$types
   );
   let builders = [
-    TypeBuilder<(ins "DITypeAttr":$resultType,
-                     "ArrayRef<DITypeAttr>":$argumentTypes), [{
-      return $_get($_ctxt, /*callingConvention=*/0, resultType, argumentTypes);
+    TypeBuilder<(ins "ArrayRef<DITypeAttr>":$types), [{
+      return $_get($_ctxt, /*callingConvention=*/0, types);
     }]>
   ];
   let assemblyFormat = "`<` struct(params) `>`";
+  let genVerifyDecl = 1;
 }
 
 #endif // LLVMIR_ATTRDEFS
index 2bf1cab..83e040a 100644 (file)
@@ -14,6 +14,7 @@
 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/DialectImplementation.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/TypeSwitch.h"
 #include "llvm/BinaryFormat/Dwarf.h"
@@ -41,11 +42,11 @@ void LLVMDialect::registerAttributes() {
 //===----------------------------------------------------------------------===//
 
 bool DINodeAttr::classof(Attribute attr) {
-  return llvm::isa<DIBasicTypeAttr, DICompileUnitAttr, DICompositeTypeAttr,
-                   DIDerivedTypeAttr, DIFileAttr, DILexicalBlockAttr,
-                   DILexicalBlockFileAttr, DILocalVariableAttr,
-                   DISubprogramAttr, DISubrangeAttr, DISubroutineTypeAttr>(
-      attr);
+  return llvm::isa<DIVoidResultTypeAttr, DIBasicTypeAttr, DICompileUnitAttr,
+                   DICompositeTypeAttr, DIDerivedTypeAttr, DIFileAttr,
+                   DILexicalBlockAttr, DILexicalBlockFileAttr,
+                   DILocalVariableAttr, DISubprogramAttr, DISubrangeAttr,
+                   DISubroutineTypeAttr>(attr);
 }
 
 //===----------------------------------------------------------------------===//
@@ -63,8 +64,23 @@ bool DIScopeAttr::classof(Attribute attr) {
 //===----------------------------------------------------------------------===//
 
 bool DITypeAttr::classof(Attribute attr) {
-  return llvm::isa<DIBasicTypeAttr, DICompositeTypeAttr, DIDerivedTypeAttr,
-                   DISubroutineTypeAttr>(attr);
+  return llvm::isa<DIVoidResultTypeAttr, DIBasicTypeAttr, DICompositeTypeAttr,
+                   DIDerivedTypeAttr, DISubroutineTypeAttr>(attr);
+}
+
+//===----------------------------------------------------------------------===//
+// DISubroutineTypeAttr
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+DISubroutineTypeAttr::verify(function_ref<InFlightDiagnostic()> emitError,
+                             unsigned int callingConventions,
+                             ArrayRef<DITypeAttr> types) {
+  if (llvm::any_of(llvm::drop_begin(types), [](DITypeAttr type) {
+        return type.isa<DIVoidResultTypeAttr>();
+      }))
+    return emitError() << "expected subroutine to have non-void argument types";
+  return success();
 }
 
 //===----------------------------------------------------------------------===//
index bb47aeb..505addf 100644 (file)
@@ -2810,10 +2810,10 @@ struct LLVMOpAsmDialectInterface : public OpAsmDialectInterface {
 
   AliasResult getAlias(Attribute attr, raw_ostream &os) const override {
     return TypeSwitch<Attribute, AliasResult>(attr)
-        .Case<DIBasicTypeAttr, DICompileUnitAttr, DICompositeTypeAttr,
-              DIDerivedTypeAttr, DIFileAttr, DILexicalBlockAttr,
-              DILexicalBlockFileAttr, DILocalVariableAttr, DISubprogramAttr,
-              DISubroutineTypeAttr>([&](auto attr) {
+        .Case<DIVoidResultTypeAttr, DIBasicTypeAttr, DICompileUnitAttr,
+              DICompositeTypeAttr, DIDerivedTypeAttr, DIFileAttr,
+              DILexicalBlockAttr, DILexicalBlockFileAttr, DILocalVariableAttr,
+              DISubprogramAttr, DISubroutineTypeAttr>([&](auto attr) {
           os << decltype(attr)::getMnemonic();
           return AliasResult::OverridableAlias;
         })
index dac737d..343ec92 100644 (file)
@@ -129,15 +129,19 @@ DISubrangeAttr DebugImporter::translateImpl(llvm::DISubrange *node) {
 
 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));
+  SmallVector<DITypeAttr> types;
+  for (llvm::DIType *type : node->getTypeArray()) {
+    if (!type) {
+      // A nullptr entry at the beginning of the subroutine types list models a
+      // void result type. Translate the nullptr to an explicit
+      // DIVoidResultTypeAttr since the attribute list cannot contain a nullptr
+      // entry.
+      types.push_back(DIVoidResultTypeAttr::get(context));
+      continue;
+    }
+    types.push_back(translate(type));
   }
-  return DISubroutineTypeAttr::get(context, node->getCC(), resultType,
-                                   argumentTypes);
+  return DISubroutineTypeAttr::get(context, node->getCC(), types);
 }
 
 DITypeAttr DebugImporter::translateImpl(llvm::DIType *node) {
index 2423e95..96f8880 100644 (file)
@@ -88,6 +88,14 @@ void DebugTranslation::translate(LLVMFuncOp func, llvm::Function &llvmFunc) {
 // Attributes
 //===----------------------------------------------------------------------===//
 
+llvm::DIType *DebugTranslation::translateImpl(DIVoidResultTypeAttr attr) {
+  // A DIVoidResultTypeAttr at the beginning of the subroutine types list models
+  // a void result type. Translate the explicit DIVoidResultTypeAttr to a
+  // nullptr since LLVM IR metadata does not have an explicit void result type
+  // representation.
+  return nullptr;
+}
+
 llvm::DIBasicType *DebugTranslation::translateImpl(DIBasicTypeAttr attr) {
   return llvm::DIBasicType::get(
       llvmCtx, attr.getTag(), attr.getName(), attr.getSizeInBits(),
@@ -201,8 +209,8 @@ llvm::DISubrange *DebugTranslation::translateImpl(DISubrangeAttr attr) {
 llvm::DISubroutineType *
 DebugTranslation::translateImpl(DISubroutineTypeAttr attr) {
   // Concatenate the result and argument types into a single array.
-  SmallVector<llvm::Metadata *> types = {translate(attr.getResultType())};
-  for (DITypeAttr type : attr.getArgumentTypes())
+  SmallVector<llvm::Metadata *> types;
+  for (DITypeAttr type : attr.getTypes())
     types.push_back(translate(type));
   return llvm::DISubroutineType::get(
       llvmCtx, llvm::DINode::FlagZero, attr.getCallingConvention(),
@@ -222,10 +230,10 @@ llvm::DINode *DebugTranslation::translate(DINodeAttr attr) {
 
   llvm::DINode *node =
       TypeSwitch<DINodeAttr, llvm::DINode *>(attr)
-          .Case<DIBasicTypeAttr, DICompileUnitAttr, DICompositeTypeAttr,
-                DIDerivedTypeAttr, DIFileAttr, DILexicalBlockAttr,
-                DILexicalBlockFileAttr, DILocalVariableAttr, DISubprogramAttr,
-                DISubrangeAttr, DISubroutineTypeAttr>(
+          .Case<DIVoidResultTypeAttr, DIBasicTypeAttr, DICompileUnitAttr,
+                DICompositeTypeAttr, DIDerivedTypeAttr, DIFileAttr,
+                DILexicalBlockAttr, DILexicalBlockFileAttr, DILocalVariableAttr,
+                DISubprogramAttr, DISubrangeAttr, DISubroutineTypeAttr>(
               [&](auto attr) { return translateImpl(attr); });
   attrToNode.insert({attr, node});
   return node;
index 79f1326..f9c87ba 100644 (file)
@@ -61,6 +61,7 @@ private:
   llvm::DIFile *translateFile(StringRef fileName);
 
   /// Translate the given attribute to the corresponding llvm debug metadata.
+  llvm::DIType *translateImpl(DIVoidResultTypeAttr attr);
   llvm::DIBasicType *translateImpl(DIBasicTypeAttr attr);
   llvm::DICompileUnit *translateImpl(DICompileUnitAttr attr);
   llvm::DICompositeType *translateImpl(DICompositeTypeAttr attr);
index ff03643..4e13c5e 100644 (file)
@@ -9,6 +9,9 @@
   isOptimized = true, emissionKind = Full
 >
 
+// CHECK-DAG: #[[VOID:.*]] = #llvm.di_void_result_type
+#void = #llvm.di_void_result_type
+
 // 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.
   flags = "TypePassByReference|NonTrivial"
 >
 
-// CHECK-DAG: #[[SPTYPE0:.*]] = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, argumentTypes = #[[INT0]], #[[PTR0]], #[[PTR1]], #[[COMP0:.*]], #[[COMP1:.*]], #[[COMP2:.*]]>
+// CHECK-DAG: #[[SPTYPE0:.*]] = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, types = #[[VOID]], #[[INT0]], #[[PTR0]], #[[PTR1]], #[[COMP0:.*]], #[[COMP1:.*]], #[[COMP2:.*]]>
 #spType0 = #llvm.di_subroutine_type<
-  callingConvention = DW_CC_normal, argumentTypes = #int0, #ptr0, #ptr1, #comp0, #comp1, #comp2
+  callingConvention = DW_CC_normal, types = #void, #int0, #ptr0, #ptr1, #comp0, #comp1, #comp2
 >
 
-// CHECK-DAG: #[[SPTYPE1:.*]] = #llvm.di_subroutine_type<resultType = #[[INT1]], argumentTypes = #[[INT1]]>
+// CHECK-DAG: #[[SPTYPE1:.*]] = #llvm.di_subroutine_type<types = #[[INT1]], #[[INT1]]>
 #spType1 = #llvm.di_subroutine_type<
   // Omit the optional callingConvention parameter.
-  resultType = #int1, argumentTypes = #int1
+  types = #int1, #int1
+>
+
+// CHECK-DAG: #[[SPTYPE2:.*]] = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
+#spType2 = #llvm.di_subroutine_type<
+  // Omit the optional types parameter array.
+  callingConvention = DW_CC_normal
 >
 
 // 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]]>
   file = #file, subprogramFlags = "Definition", type = #spType1
 >
 
+// CHECK-DAG: #[[SP2:.*]] = #llvm.di_subprogram<compileUnit = #[[CU]], scope = #[[FILE]], name = "value", file = #[[FILE]], subprogramFlags = Definition, type = #[[SPTYPE2]]>
+#sp2 = #llvm.di_subprogram<
+  // Omit the optional linkageName parameter.
+  compileUnit = #cu, scope = #file, name = "value",
+  file = #file, subprogramFlags = "Definition", type = #spType2
+>
+
 // CHECK-DAG: #[[BLOCK0:.*]] = #llvm.di_lexical_block<scope = #[[SP0]], line = 1, column = 2>
 #block0 = #llvm.di_lexical_block<scope = #sp0, line = 1, column = 2>
 
 // CHECK-DAG: #[[BLOCK1:.*]] = #llvm.di_lexical_block<scope = #[[SP1]]>
 #block1 = #llvm.di_lexical_block<scope = #sp1>
 
+// CHECK-DAG: #[[BLOCK2:.*]] = #llvm.di_lexical_block<scope = #[[SP2]]>
+#block2 = #llvm.di_lexical_block<scope = #sp2>
+
 // CHECK-DAG: #[[VAR0:.*]] = #llvm.di_local_variable<scope = #[[BLOCK0]], name = "alloc", file = #[[FILE]], line = 6, arg = 1, alignInBits = 32, type = #[[INT0]]>
 #var0 = #llvm.di_local_variable<
   scope = #block0, name = "alloc", file = #file,
   line = 6, arg = 1, alignInBits = 32, type = #int0
 >
 
-// CHECK-DAG: #[[VAR1:.*]] = #llvm.di_local_variable<scope = #[[BLOCK1]], name = "arg">
+// CHECK-DAG: #[[VAR1:.*]] = #llvm.di_local_variable<scope = #[[BLOCK1]], name = "arg1">
 #var1 = #llvm.di_local_variable<
   // Omit the optional parameters.
-  scope = #block1, name = "arg"
+  scope = #block1, name = "arg1"
+>
+
+// CHECK-DAG: #[[VAR2:.*]] = #llvm.di_local_variable<scope = #[[BLOCK2]], name = "arg2">
+#var2 = #llvm.di_local_variable<
+  // Omit the optional parameters.
+  scope = #block2, name = "arg2"
 >
 
 // CHECK: llvm.func @addr(%[[ARG:.*]]: i64)
@@ -108,9 +133,11 @@ llvm.func @addr(%arg: i64) {
   llvm.return
 }
 
-// CHECK: llvm.func @value(%[[ARG:.*]]: i32)
-llvm.func @value(%arg: i32) -> i32 {
-  // CHECK: llvm.intr.dbg.value #[[VAR1]] = %[[ARG]]
-  llvm.intr.dbg.value #var1 = %arg : i32
-  llvm.return %arg : i32
+// CHECK: llvm.func @value(%[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32)
+llvm.func @value(%arg1: i32, %arg2: i32) {
+  // CHECK: llvm.intr.dbg.value #[[VAR1]] = %[[ARG1]]
+  llvm.intr.dbg.value #var1 = %arg1 : i32
+  // CHECK: llvm.intr.dbg.value #[[VAR2]] = %[[ARG2]]
+  llvm.intr.dbg.value #var2 = %arg2 : i32
+  llvm.return
 }
index 1a50afa..e863bd3 100644 (file)
@@ -1392,3 +1392,9 @@ func.func @extract_scalable_from_fixed_length_vector(%arg0 : vector<16xf32>) {
   // expected-error@+1 {{op failed to verify that it is not extracting scalable from fixed-length vectors.}}
   %0 = llvm.intr.vector.extract %arg0[0] : vector<[8]xf32> from vector<16xf32>
 }
+
+// -----
+
+#void = #llvm.di_void_result_type
+// expected-error@below {{expected subroutine to have non-void argument types}}
+#void_argument_type = #llvm.di_subroutine_type<types = #void, #void>
index 4a8a43b..2888b7a 100644 (file)
@@ -112,9 +112,10 @@ define i32 @lexical_block_file(i32 %arg1) {
 
 ; // -----
 
-; 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]]>
+; CHECK-DAG: #[[VOID:.+]] = #llvm.di_void_result_type
+; CHECK-DAG: #[[INT1:.+]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "int1">
+; CHECK-DAG: #[[INT2:.+]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "int2", sizeInBits = 32, encoding = DW_ATE_signed>
+; CHECK-DAG: #llvm.di_subroutine_type<types = #[[VOID]], #[[INT1]], #[[INT2]]>
 
 define void @basic_type() !dbg !3 {
   ret void
@@ -136,7 +137,7 @@ define void @basic_type() !dbg !3 {
 ; 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]]>
 ; 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]]>
+; CHECK: #llvm.di_subroutine_type<types = #[[PTR1]], #[[PTR2]]>
 
 define void @derived_type() !dbg !3 {
   ret void
@@ -149,7 +150,7 @@ define void @derived_type() !dbg !3 {
 !2 = !DIFile(filename: "debug-info.ll", directory: "/")
 !3 = distinct !DISubprogram(name: "derived_type", scope: !2, file: !2, spFlags: DISPFlagDefinition, unit: !1, type: !4)
 !4 = !DISubroutineType(types: !5)
-!5 = !{null, !7, !8}
+!5 = !{!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)
@@ -162,7 +163,7 @@ define void @derived_type() !dbg !3 {
 ; CHECK-DAG: #[[COMP2:.+]] = #llvm.di_composite_type<{{.*}}, file = #[[FILE]], scope = #[[FILE]], baseType = #[[INT]]>
 ; CHECK-DAG: #[[COMP3:.+]] = #llvm.di_composite_type<{{.*}}, flags = Vector, elements = #llvm.di_subrange<count = 4 : i64>>
 ; CHECK-DAG: #[[COMP4:.+]] = #llvm.di_composite_type<{{.*}}, flags = Vector, elements = #llvm.di_subrange<lowerBound = 0 : i64, upperBound = 4 : i64, stride = 1 : i64>>
-; CHECK-DAG: #llvm.di_subroutine_type<argumentTypes = #[[COMP1]], #[[COMP2]], #[[COMP3]], #[[COMP4]]>
+; CHECK-DAG: #llvm.di_subroutine_type<types = #[[COMP1]], #[[COMP2]], #[[COMP3]], #[[COMP4]]>
 
 define void @composite_type() !dbg !3 {
   ret void
@@ -175,7 +176,7 @@ define void @composite_type() !dbg !3 {
 !2 = !DIFile(filename: "debug-info.ll", directory: "/")
 !3 = distinct !DISubprogram(name: "composite_type", scope: !2, file: !2, spFlags: DISPFlagDefinition, unit: !1, type: !4)
 !4 = !DISubroutineType(types: !5)
-!5 = !{null, !7, !8, !9, !10}
+!5 = !{!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)
@@ -188,11 +189,11 @@ define void @composite_type() !dbg !3 {
 
 ; // -----
 
-; 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]]>
+; CHECK-DAG: #[[FILE:.+]] = #llvm.di_file<"debug-info.ll" in "/">
+; CHECK-DAG: #[[CU:.+]] = #llvm.di_compile_unit<sourceLanguage = DW_LANG_C, file = #[[FILE]], producer = "", isOptimized = false, emissionKind = None>
+; Verify an empty subroutine types list is supported.
+; CHECK-DAG: #[[SP_TYPE:.+]] = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
+; CHECK-DAG: #[[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
@@ -205,7 +206,7 @@ define void @subprogram() !dbg !3 {
 !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}
+!5 = !{}
 !6 = !DIBasicType(name: "int")
 
 ; // -----
index 434207e..50d18eb 100644 (file)
@@ -48,24 +48,31 @@ llvm.func @func_no_debug() {
   baseType = #si64, flags = Vector,
   elements = #llvm.di_subrange<lowerBound = 0, upperBound = 4, stride = 1>
 >
-#spType = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, argumentTypes = #si64, #ptr, #named, #composite, #vector>
-#sp = #llvm.di_subprogram<
+#void = #llvm.di_void_result_type
+#spType0 = #llvm.di_subroutine_type<callingConvention = DW_CC_normal, types = #void, #si64, #ptr, #named, #composite, #vector>
+#sp0 = #llvm.di_subprogram<
   compileUnit = #cu, scope = #file, name = "func_with_debug", linkageName = "func_with_debug",
-  file = #file, line = 3, scopeLine = 3, subprogramFlags = "Definition|Optimized", type = #spType
+  file = #file, line = 3, scopeLine = 3, subprogramFlags = "Definition|Optimized", type = #spType0
 >
 #calleeType = #llvm.di_subroutine_type<
-  // Omit the optional callingConvention parameter but specify a result type.
-  resultType = #si64, argumentTypes = #si64>
+  // Omit the optional callingConvention parameter.
+  types = #si64, #si64>
 #callee = #llvm.di_subprogram<
   // Omit the optional linkageName, line, and scopeLine parameters.
   compileUnit = #cu, scope = #composite, name = "callee",
   file = #file, subprogramFlags = "Definition", type = #calleeType
 >
-#fileScope = #llvm.di_lexical_block_file<scope = #sp, file = #file, discriminator = 0>
-#blockScope = #llvm.di_lexical_block<scope = #sp>
+#fileScope = #llvm.di_lexical_block_file<scope = #sp0, file = #file, discriminator = 0>
+#blockScope = #llvm.di_lexical_block<scope = #sp0>
 #variable = #llvm.di_local_variable<scope = #fileScope, name = "arg", file = #file, line = 6, arg = 1, alignInBits = 32, type = #si64>
 #variableAddr = #llvm.di_local_variable<scope = #blockScope, name = "alloc">
 
+#spType1 = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
+#sp1 = #llvm.di_subprogram<
+  compileUnit = #cu, scope = #file, name = "empty_types",
+  file = #file, subprogramFlags = "Definition", type = #spType1
+>
+
 // CHECK-LABEL: define void @func_with_debug(
 // CHECK-SAME: i64 %[[ARG:.*]]) !dbg ![[FUNC_LOC:[0-9]+]]
 llvm.func @func_with_debug(%arg: i64) {
@@ -93,10 +100,15 @@ llvm.func @func_with_debug(%arg: i64) {
   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])])
+  %sum = llvm.add %arg, %arg : i64 loc(fused<#callee>[callsite("foo.mlir":2:4 at fused<#sp0>["foo.mlir":28:5])])
 
   llvm.return
-} loc(fused<#sp>["foo.mlir":1:1])
+} loc(fused<#sp0>["foo.mlir":1:1])
+
+// CHECK: define void @empty_types() !dbg ![[EMPTY_TYPES_LOC:[0-9]+]]
+llvm.func @empty_types() {
+  llvm.return
+} loc(fused<#sp1>["foo.mlir":2: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/")
@@ -131,3 +143,7 @@ llvm.func @func_with_debug(%arg: i64) {
 // CHECK: ![[CALLEE_TYPE]] = !DISubroutineType(types: ![[CALLEE_ARGS:.*]])
 // CHECK: ![[CALLEE_ARGS]] = !{![[ARG_TYPE:.*]], ![[ARG_TYPE:.*]]}
 // CHECK: ![[INLINE_LOC]] = !DILocation(line: 28, column: 5,
+
+// CHECK: ![[EMPTY_TYPES_LOC]] = distinct !DISubprogram(name: "empty_types", scope: ![[CU_FILE_LOC]], file: ![[CU_FILE_LOC]], type: ![[EMPTY_TYPES_TYPE:.*]], spFlags: DISPFlagDefinition
+// CHECK: ![[EMPTY_TYPES_TYPE]] = !DISubroutineType(cc: DW_CC_normal, types: ![[EMPTY_TYPES_ARGS:.*]])
+// CHECK: ![[EMPTY_TYPES_ARGS]] = !{}