[llvm] Get intrinsic info table entries refactor 1 1 (PR #196802)

Rahul Joshi via llvm-commits llvm-commits at lists.llvm.org
Tue May 12 22:32:11 PDT 2026


https://github.com/jurahul updated https://github.com/llvm/llvm-project/pull/196802

>From 84ef8c2773b3701b23521860b3a5c55ee8a611b0 Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Sun, 10 May 2026 06:54:50 -0700
Subject: [PATCH] More Precise error messages

---
 llvm/lib/IR/Intrinsics.cpp                    | 213 +++++++++++---
 llvm/test/Verifier/intrinsic-bad-arg-type1.ll | 277 ++++++++++++++++++
 2 files changed, 444 insertions(+), 46 deletions(-)
 create mode 100644 llvm/test/Verifier/intrinsic-bad-arg-type1.ll

diff --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index 89aef32938c75..a5bb408cdefbc 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -844,81 +844,182 @@ bool Intrinsic::hasConstrainedFPRoundingModeOperand(Intrinsic::ID QID) {
   }
 }
 
-using DeferredIntrinsicMatchPair =
-    std::pair<Type *, ArrayRef<Intrinsic::IITDescriptor>>;
+// The function below prints an error message when the type does not match. The
+// error message is of the form: intrinsic <position> type <reason>. The
+// `position` indiciates which type or type component of the intrinsic signature
+// the error is about and can be of the following forms:
+//
+//   return  -> for return type.
+//   return struct element 3 ->
+//   return vector element
+//   return struct element 3 vector element
+//   argument 3
+//   argument 3 vector element
+//
+// To support deferred checks also being able to generate these error messages
+// we need to encode the position compactly so that it can be stashed into
+// DeferredIntrinsicMatchPair below.
+//
+namespace {
+struct MatchPosition {
+  unsigned IsRet : 1;
+  unsigned Num : 31; // Argument number (when IsRet = false).
+  struct Index {
+    unsigned IsStruct : 1; // Is this a struct element or vector element?
+    unsigned Num : 31;     // Struct element index.
+  };
+  // We expect this to be just 2 levels deep, since nested structs are not
+  // supported.
+  static constexpr unsigned INDEX_TABLE_SIZE = 2;
+  Index Indices[INDEX_TABLE_SIZE];
+  unsigned NumIndices = 0;
+
+  void pop_index() {
+    assert(NumIndices > 0 && "cannot pop from empty indices");
+    --NumIndices;
+  }
+
+  void push_struct_element(unsigned ElementNum) {
+    assert(NumIndices < INDEX_TABLE_SIZE && "index table overflow");
+    Indices[NumIndices].IsStruct = true;
+    Indices[NumIndices++].Num = ElementNum;
+  }
+
+  void push_vector_element() {
+    assert(NumIndices < INDEX_TABLE_SIZE && "index table overflow");
+    Indices[NumIndices].IsStruct = false;
+    Indices[NumIndices++].Num = 0;
+  }
+};
+} // namespace
+
+static raw_ostream &operator<<(raw_ostream &OS, const MatchPosition &Pos) {
+  OS << "intrinsic ";
+
+  if (Pos.IsRet)
+    OS << "return";
+  else
+    OS << "argument " << Pos.Num;
+
+  for (const MatchPosition::Index &Idx :
+       ArrayRef(Pos.Indices).take_front(Pos.NumIndices)) {
+    if (Idx.IsStruct)
+      OS << " struct element " << Idx.Num;
+    else
+      OS << " vector element";
+  }
+  return OS;
+}
+
+using DeferredIntrinsicMatchInfo =
+    std::tuple<Type *, ArrayRef<Intrinsic::IITDescriptor>, MatchPosition>;
 
 static bool
 matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
-                   SmallVectorImpl<Type *> &OverloadTys,
-                   SmallVectorImpl<DeferredIntrinsicMatchPair> &DeferredChecks,
-                   bool IsDeferredCheck) {
+                   MatchPosition Position, SmallVectorImpl<Type *> &OverloadTys,
+                   SmallVectorImpl<DeferredIntrinsicMatchInfo> &DeferredChecks,
+                   bool IsDeferredCheck, raw_ostream &OS) {
   using namespace Intrinsic;
 
   // If we ran out of descriptors, there are too many arguments.
-  if (Infos.empty())
+  if (Infos.empty()) {
+    OS << Position << " too many "
+       << (Position.IsRet ? "returns" : "arguments");
     return true;
+  }
 
   // Do this before slicing off the 'front' part
   auto InfosRef = Infos;
-  auto DeferCheck = [&DeferredChecks, &InfosRef](Type *T) {
-    DeferredChecks.emplace_back(T, InfosRef);
+  auto DeferCheck = [&DeferredChecks, &InfosRef, &Position](Type *T) {
+    DeferredChecks.emplace_back(T, InfosRef, Position);
     return false;
   };
 
   IITDescriptor D = Infos.consume_front();
 
+  auto PrintMsg = [&OS, &Position, Ty](bool IsValid,
+                                       const Twine &Expected) -> bool {
+    if (IsValid)
+      return false;
+    OS << Position << " type expected " << Expected << ", but got " << *Ty;
+    return true;
+  };
+
   switch (D.Kind) {
   case IITDescriptor::Void:
-    return !Ty->isVoidTy();
+    assert(Position.IsRet && Position.NumIndices == 0 &&
+           "void descriptor expected only for return type");
+    return PrintMsg(Ty->isVoidTy(), "void");
   case IITDescriptor::MMX: {
     FixedVectorType *VT = dyn_cast<FixedVectorType>(Ty);
-    return !VT || VT->getNumElements() != 1 ||
-           !VT->getElementType()->isIntegerTy(64);
+    return PrintMsg(VT && VT->getNumElements() == 1 &&
+                        VT->getElementType()->isIntegerTy(64),
+                    "x86_mmx (<1 x i64>)");
   }
   case IITDescriptor::AMX:
-    return !Ty->isX86_AMXTy();
+    return PrintMsg(Ty->isX86_AMXTy(), "x86_amx");
   case IITDescriptor::Token:
-    return !Ty->isTokenTy();
+    return PrintMsg(Ty->isTokenTy(), "token");
   case IITDescriptor::Metadata:
-    return !Ty->isMetadataTy();
+    return PrintMsg(Ty->isMetadataTy(), "metadata");
   case IITDescriptor::Half:
-    return !Ty->isHalfTy();
+    return PrintMsg(Ty->isHalfTy(), "half");
   case IITDescriptor::BFloat:
-    return !Ty->isBFloatTy();
+    return PrintMsg(Ty->isBFloatTy(), "bfloat");
   case IITDescriptor::Float:
-    return !Ty->isFloatTy();
+    return PrintMsg(Ty->isFloatTy(), "float");
   case IITDescriptor::Double:
-    return !Ty->isDoubleTy();
+    return PrintMsg(Ty->isDoubleTy(), "double");
   case IITDescriptor::Quad:
-    return !Ty->isFP128Ty();
+    return PrintMsg(Ty->isFP128Ty(), "fp128");
   case IITDescriptor::PPCQuad:
-    return !Ty->isPPC_FP128Ty();
+    return PrintMsg(Ty->isPPC_FP128Ty(), "ppc_fp128");
   case IITDescriptor::Integer:
-    return !Ty->isIntegerTy(D.IntegerWidth);
+    return PrintMsg(Ty->isIntegerTy(D.IntegerWidth),
+                    "i" + Twine(D.IntegerWidth));
   case IITDescriptor::AArch64Svcount:
-    return !isa<TargetExtType>(Ty) ||
-           cast<TargetExtType>(Ty)->getName() != "aarch64.svcount";
+    return PrintMsg(isa<TargetExtType>(Ty) &&
+                        cast<TargetExtType>(Ty)->getName() == "aarch64.svcount",
+                    "aarch64.svcount");
   case IITDescriptor::Vector: {
     VectorType *VT = dyn_cast<VectorType>(Ty);
-    return !VT || VT->getElementCount() != D.VectorWidth ||
-           matchIntrinsicType(VT->getElementType(), Infos, OverloadTys,
-                              DeferredChecks, IsDeferredCheck);
+    StringRef Scalable = D.VectorWidth.isScalable() ? "vscale " : "";
+    bool HasError =
+        PrintMsg(VT && VT->getElementCount() == D.VectorWidth,
+                 Twine(Scalable) + "vector with " +
+                     Twine(D.VectorWidth.getKnownMinValue()) + " elements");
+    if (HasError)
+      return true;
+    Position.push_vector_element();
+    return matchIntrinsicType(VT->getElementType(), Infos, Position,
+                              OverloadTys, DeferredChecks, IsDeferredCheck, OS);
   }
   case IITDescriptor::Pointer: {
     PointerType *PT = dyn_cast<PointerType>(Ty);
-    return !PT || PT->getAddressSpace() != D.PointerAddressSpace;
+    unsigned AS = D.PointerAddressSpace;
+    bool IsValid = PT && PT->getAddressSpace() == AS;
+    if (AS == 0)
+      return PrintMsg(IsValid, "ptr");
+    else
+      return PrintMsg(IsValid, "ptr addrspace(" + Twine(AS) + ")");
   }
 
   case IITDescriptor::Struct: {
     StructType *ST = dyn_cast<StructType>(Ty);
-    if (!ST || !ST->isLiteral() || ST->isPacked() ||
-        ST->getNumElements() != D.StructNumElements)
+    unsigned EC = D.StructNumElements;
+    bool HasError = PrintMsg(
+        ST && ST->isLiteral() && !ST->isPacked() && ST->getNumElements() == EC,
+        "literal non-packed struct with " + Twine(EC) + " elements");
+    if (HasError)
       return true;
 
-    for (unsigned i = 0, e = D.StructNumElements; i != e; ++i)
-      if (matchIntrinsicType(ST->getElementType(i), Infos, OverloadTys,
-                             DeferredChecks, IsDeferredCheck))
+    for (const auto &[Idx, ETy] : llvm::enumerate(ST->elements())) {
+      Position.push_struct_element(Idx);
+      if (matchIntrinsicType(ETy, Infos, Position, OverloadTys, DeferredChecks,
+                             IsDeferredCheck, OS))
         return true;
+      Position.pop_index();
+    }
     return false;
   }
 
@@ -997,9 +1098,10 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
       if (ReferenceType->getElementCount() != ThisArgType->getElementCount())
         return true;
       EltTy = ThisArgType->getElementType();
+      Position.push_vector_element();
     }
-    return matchIntrinsicType(EltTy, Infos, OverloadTys, DeferredChecks,
-                              IsDeferredCheck);
+    return matchIntrinsicType(EltTy, Infos, Position, OverloadTys,
+                              DeferredChecks, IsDeferredCheck, OS);
   }
   case IITDescriptor::VecOfAnyPtrsToElt: {
     unsigned RefOverloadIndex = D.getRefOverloadIndex();
@@ -1079,10 +1181,26 @@ static bool isSignatureValid(FunctionType *FTy,
                              unsigned NumArgs, bool IsVarArg,
                              SmallVectorImpl<Type *> &OverloadTys,
                              raw_ostream &OS) {
-  SmallVector<DeferredIntrinsicMatchPair, 2> DeferredChecks;
-  if (matchIntrinsicType(FTy->getReturnType(), Infos, OverloadTys,
-                         DeferredChecks, false)) {
-    OS << "intrinsic has incorrect return type!";
+  SmallVector<DeferredIntrinsicMatchInfo, 2> DeferredChecks;
+
+  assert(!Infos.empty() && "Table consistency error");
+
+  MatchPosition Pos;
+  Pos.IsRet = true;
+
+  // Temporary fix to print an error message if `matchIntrinsicType` fails but
+  // does not print an error message. This will be removed once all failing
+  // cases in `matchIntrinsicType` start generating error messages.
+  uint64_t OSPos = OS.tell();
+  auto PrintMsg = [OSPos, &OS](StringRef Msg) {
+    if (OS.tell() != OSPos)
+      return;
+    OS << Msg;
+  };
+
+  if (matchIntrinsicType(FTy->getReturnType(), Infos, Pos, OverloadTys,
+                         DeferredChecks, false, OS)) {
+    PrintMsg("intrinsic has incorrect return type!");
     return false;
   }
   unsigned NumDeferredReturnChecks = DeferredChecks.size();
@@ -1093,22 +1211,25 @@ static bool isSignatureValid(FunctionType *FTy,
     return false;
   }
 
-  for (Type *Ty : FTy->params()) {
-    if (matchIntrinsicType(Ty, Infos, OverloadTys, DeferredChecks, false)) {
-      OS << "intrinsic has incorrect argument type!";
+  Pos.IsRet = false;
+  for (const auto &[Idx, Ty] : llvm::enumerate(FTy->params())) {
+    Pos.Num = Idx;
+    if (matchIntrinsicType(Ty, Infos, Pos, OverloadTys, DeferredChecks, false,
+                           OS)) {
+      PrintMsg("intrinsic has incorrect argument type!");
       return false;
     }
   }
 
   for (unsigned I = 0, E = DeferredChecks.size(); I != E; ++I) {
-    DeferredIntrinsicMatchPair &Check = DeferredChecks[I];
-    if (!matchIntrinsicType(Check.first, Check.second, OverloadTys,
-                            DeferredChecks, true))
+    auto &[DefTy, DefInfos, DefPosition] = DeferredChecks[I];
+    if (!matchIntrinsicType(DefTy, DefInfos, DefPosition, OverloadTys,
+                            DeferredChecks, true, OS))
       continue;
     if (I < NumDeferredReturnChecks)
-      OS << "intrinsic has incorrect return type!";
+      PrintMsg("intrinsic has incorrect return type!");
     else
-      OS << "intrinsic has incorrect argument type!";
+      PrintMsg("intrinsic has incorrect argument type!");
     return false;
   }
 
diff --git a/llvm/test/Verifier/intrinsic-bad-arg-type1.ll b/llvm/test/Verifier/intrinsic-bad-arg-type1.ll
new file mode 100644
index 0000000000000..61ee7efa7aa4f
--- /dev/null
+++ b/llvm/test/Verifier/intrinsic-bad-arg-type1.ll
@@ -0,0 +1,277 @@
+; RUN: split-file %s %t
+
+;--- test0.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test0.ll | FileCheck %t/test0.ll
+; CHECK: intrinsic return type expected void, but got float
+; CHECK-NEXT: ptr @llvm.set.rounding
+
+declare float @llvm.set.rounding(i32)
+
+define void @test0() {
+entry:
+  %t = call float @llvm.set.rounding(i32 0)
+  ret void
+}
+
+;--- test1.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test1.ll | FileCheck %t/test1.ll
+; CHECK: intrinsic return type expected x86_mmx (<1 x i64>), but got x86_amx
+; CHECK-NEXT: ptr @llvm.x86.sse.cvtps2pi
+
+declare x86_amx @llvm.x86.sse.cvtps2pi(<4 x float>)
+
+define void @test1(<4 x float> %a) {
+entry:
+  %t = call x86_amx @llvm.x86.sse.cvtps2pi(<4 x float> %a)
+  ret void
+}
+
+;--- test2.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test2.ll | FileCheck %t/test2.ll
+; CHECK: intrinsic argument 1 type expected x86_mmx (<1 x i64>), but got i32
+; CHECK-NEXT: ptr @llvm.x86.sse.cvtpi2ps
+
+declare <4 x float> @llvm.x86.sse.cvtpi2ps(<4 x float>, i32)
+
+define void @test1(<4 x float> %a) {
+entry:
+  %t = call <4 x float> @llvm.x86.sse.cvtpi2ps(<4 x float> %a, i32 0)
+  ret void
+}
+
+;--- test3.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test3.ll | FileCheck %t/test3.ll
+; CHECK: intrinsic return type expected x86_amx, but got float
+; CHECK-NEXT: ptr @llvm.x86.tileloadd64.internal
+
+declare float @llvm.x86.tileloadd64.internal(i16, i16, ptr, i64)
+
+define void @test1(ptr %a) {
+entry:
+  %t = call float @llvm.x86.tileloadd64.internal(i16 0, i16 0, ptr %a, i64 0)
+  ret void
+}
+
+;--- test4.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test4.ll | FileCheck %t/test4.ll
+; CHECK: intrinsic return type expected token, but got i32
+; CHECK-NEXT: ptr @llvm.call.preallocated.setup
+
+declare i32 @llvm.call.preallocated.setup(i32)
+
+define void @test1() {
+entry:
+  %t = call i32 @llvm.call.preallocated.setup(i32 0)
+  ret void
+}
+
+;--- test5.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test5.ll | FileCheck %t/test5.ll
+; CHECK: intrinsic argument 1 type expected metadata, but got i16
+; CHECK-NEXT: ptr @llvm.fptrunc.round.f64.f64
+
+declare double @llvm.fptrunc.round.f64.f64(double, i16)
+
+define void @test1() {
+entry:
+  %t = call double @llvm.fptrunc.round.f64.f64(double 1.0, i16 0)
+  ret void
+}
+
+;--- test6.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test6.ll | FileCheck %t/test6.ll
+; CHECK: intrinsic argument 0 type expected half, but got i16
+; CHECK-NEXT: ptr @llvm.nvvm.mul.rn.sat.f16
+
+declare half @llvm.nvvm.mul.rn.sat.f16(i16, half)
+
+define void @test1() {
+entry:
+  %t = call half @llvm.nvvm.mul.rn.sat.f16(i16 0, half 1.0)
+  ret void
+}
+
+;--- test7.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test7.ll | FileCheck %t/test7.ll
+; CHECK: intrinsic return type expected bfloat, but got half
+; CHECK-NEXT: ptr @llvm.arm.neon.vcvtbfp2bf
+
+declare half @llvm.arm.neon.vcvtbfp2bf(float)
+
+define void @test1() {
+entry:
+  %t = call half @llvm.arm.neon.vcvtbfp2bf(float 0.0)
+  ret void
+}
+
+;--- test8.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test8.ll | FileCheck %t/test8.ll
+; CHECK: intrinsic argument 2 type expected float, but got half
+; CHECK-NEXT: ptr @llvm.x86.avx512.vfmadd.f32
+
+declare float @llvm.x86.avx512.vfmadd.f32(float, float, half, i32)
+
+define void @test1() {
+entry:
+  %t = call float @llvm.x86.avx512.vfmadd.f32(float 0.0, float 0.0, half 0.0, i32 0)
+  ret void
+}
+
+;--- test9.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test9.ll | FileCheck %t/test9.ll
+; CHECK: intrinsic argument 2 type expected double, but got half
+; CHECK-NEXT: ptr @llvm.expect.with.probability.i32
+
+declare i32 @llvm.expect.with.probability.i32(i32, i32, half)
+
+define void @test1() {
+entry:
+  %t = call i32 @llvm.expect.with.probability.i32(i32 0, i32 0, half 0.0)
+  ret void
+}
+
+;--- test10.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test10.ll | FileCheck %t/test10.ll
+; CHECK: intrinsic argument 0 type expected fp128, but got double
+; CHECK-NEXT: ptr @llvm.ppc.truncf128.round.to.odd
+
+declare double @llvm.ppc.truncf128.round.to.odd(double)
+
+define void @test1() {
+entry:
+  %t = call double @llvm.ppc.truncf128.round.to.odd(double 0.0)
+  ret void
+}
+
+;--- test11.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test11.ll | FileCheck %t/test11.ll
+; CHECK: intrinsic argument 0 type expected ppc_fp128, but got double
+; CHECK-NEXT: ptr  @llvm.ppc.unpack.longdouble
+
+declare double @llvm.ppc.unpack.longdouble(double, i32)
+
+define void @test1() {
+entry:
+  %t = call double @llvm.ppc.unpack.longdouble(double 0.0, i32 0)
+  ret void
+}
+
+;--- test12.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test12.ll | FileCheck %t/test12.ll
+; CHECK: intrinsic return type expected i64, but got double
+; CHECK-NEXT: ptr @llvm.readcyclecounter
+
+declare double @llvm.readcyclecounter()
+
+define void @test1() {
+entry:
+  %t = call double @llvm.readcyclecounter()
+  ret void
+}
+
+;--- test13.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test13.ll | FileCheck %t/test13.ll
+; CHECK: intrinsic argument 0 type expected aarch64.svcount, but got i32
+; CHECK-NEXT: ptr @llvm.aarch64.sve.pext
+
+declare <4 x i32> @llvm.aarch64.sve.pext(i32, i32)
+
+define void @test1() {
+entry:
+  %t = call <4 x i32> @llvm.aarch64.sve.pext(i32 0, i32 0)
+  ret void
+}
+
+;--- test14.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test14.ll | FileCheck %t/test14.ll
+; CHECK: intrinsic return type expected vector with 16 elements, but got i32
+; CHECK-NEXT: ptr @llvm.aarch64.neon.pmull64
+
+; Correct return type is <16 x i8>
+declare i32 @llvm.aarch64.neon.pmull64(i64, i64)
+
+define void @test1() {
+entry:
+  %t = call i32 @llvm.aarch64.neon.pmull64(i64 0, i64 0)
+  ret void
+}
+
+;--- test15.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test15.ll | FileCheck %t/test15.ll
+; CHECK: intrinsic return vector element type expected i8, but got i32
+; CHECK-NEXT: ptr @llvm.aarch64.neon.pmull64
+
+declare <16 x i32> @llvm.aarch64.neon.pmull64(i64, i64)
+
+define void @test1() {
+entry:
+  %t = call <16 x i32> @llvm.aarch64.neon.pmull64(i64 0, i64 0)
+  ret void
+}
+
+;--- test16.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test16.ll | FileCheck %t/test16.ll
+; CHECK: intrinsic return type expected vector with 16 elements, but got <vscale x 16 x i32>
+; CHECK-NEXT: ptr @llvm.aarch64.neon.pmull64
+
+declare <vscale x 16 x i32> @llvm.aarch64.neon.pmull64(i64, i64)
+
+define void @test1() {
+entry:
+  %t = call <vscale x 16 x i32> @llvm.aarch64.neon.pmull64(i64 0, i64 0)
+  ret void
+}
+
+;--- test17.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test17.ll | FileCheck %t/test17.ll
+; CHECK: intrinsic argument 1 type expected ptr, but got i32
+; CHECK-NEXT: ptr @llvm.gcroot
+
+declare void @llvm.gcroot(ptr, i32)
+
+define void @test1() {
+entry:
+  call void @llvm.gcroot(ptr null, i32 0)
+  ret void
+}
+
+;--- test18.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test18.ll | FileCheck %t/test18.ll
+; CHECK: intrinsic argument 0 type expected ptr addrspace(1), but got ptr
+; CHECK-NEXT: ptr @llvm.nvvm.applypriority.global.L2.evict.normal
+
+declare void @llvm.nvvm.applypriority.global.L2.evict.normal(ptr, i64)
+
+define void @test1() {
+entry:
+  call void @llvm.nvvm.applypriority.global.L2.evict.normal(ptr null, i64 0)
+  ret void
+}
+
+;--- test19.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test19.ll | FileCheck %t/test19.ll
+; CHECK: intrinsic return type expected literal non-packed struct with 2 elements, but got void
+; CHECK-NEXT: ptr @llvm.nvvm.elect.sync
+
+; Expected return type is { i32, i1 }.
+declare void @llvm.nvvm.elect.sync(i32)
+
+define void @test1() {
+entry:
+  call void @llvm.nvvm.elect.sync(i32 0)
+  ret void
+}
+
+;--- test20.ll
+; RUN: not opt -S -passes=verify 2>&1 < %t/test20.ll | FileCheck %t/test20.ll
+; CHECK: intrinsic return struct element 1 type expected i1, but got i2
+; CHECK-NEXT: ptr @llvm.nvvm.elect.sync
+
+; Expected return type is { i32, i1 }.
+declare { i32, i2 } @llvm.nvvm.elect.sync(i32)
+
+define void @test1() {
+entry:
+  call { i32, i2 } @llvm.nvvm.elect.sync(i32 0)
+  ret void
+}



More information about the llvm-commits mailing list