[llvm] [LLVM-Tablegen] Explicit Type Constraints for Overloaded LLVM Intrinsics (PR #172442)
Dharuni R Acharya via llvm-commits
llvm-commits at lists.llvm.org
Tue Dec 16 00:47:48 PST 2025
https://github.com/DharuniRAcharya created https://github.com/llvm/llvm-project/pull/172442
This patch adds LLVM infrastructure to support explicit type constraints for overloaded intrinsics.
A new constrained type class, `AnyTypeOf<[list of LLVMType]>`, is added to express allowed type
subsets directly in TableGen, enabling precise IR-level verification of intrinsic type usage.
Type violations are now detected during the LLVM Verifier flow with clear, actionable diagnostics,
improving error reporting and debuggability while keeping verification consistent with
LLVM’s existing pipelines.
>From 3a66fcb6c2d21fece7e9cb8d37f115f71aad98f9 Mon Sep 17 00:00:00 2001
From: Dharuni R Acharya <dharunira at nvidia.com>
Date: Tue, 16 Dec 2025 08:43:42 +0000
Subject: [PATCH] [LLVM-Tablegen] Explicit Type Constraints for Overloaded LLVM
Intrinsics
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch adds LLVM infrastructure to support explicit type constraints for overloaded intrinsics.
A new constrained type class, AnyTypeOf<[list of LLVMType]>, is added to express allowed
type subsets directly in TableGen, enabling precise IR-level verification of intrinsic type usage.
Type violations are now detected during the LLVM Verifier flow with clear, actionable
diagnostics, improving error reporting and debuggability while keeping verification
consistent with LLVM’s existing pipelines.
Signed-off-by: Dharuni R Acharya <dharunira at nvidia.com>
---
llvm/include/llvm/IR/Intrinsics.h | 12 +
llvm/include/llvm/IR/Intrinsics.td | 19 ++
llvm/include/llvm/IR/IntrinsicsNVVM.td | 12 +
llvm/lib/IR/Intrinsics.cpp | 297 +++++++++++++++++-
llvm/lib/IR/Verifier.cpp | 10 +
.../NVPTX/anytypeof_constraints_invalid.ll | 51 +++
.../NVPTX/anytypeof_constraints_valid.ll | 45 +++
7 files changed, 430 insertions(+), 16 deletions(-)
create mode 100644 llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll
create mode 100644 llvm/test/CodeGen/NVPTX/anytypeof_constraints_valid.ll
diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h
index 2c86a43e114ea..3d2dcd5e0da1a 100644
--- a/llvm/include/llvm/IR/Intrinsics.h
+++ b/llvm/include/llvm/IR/Intrinsics.h
@@ -181,6 +181,7 @@ namespace Intrinsic {
AMX,
PPCQuad,
AArch64Svcount,
+ ArgumentTypeConstraint, // For AnyTypeOf - marks constrained argument types.
} Kind;
union {
@@ -189,6 +190,7 @@ namespace Intrinsic {
unsigned Pointer_AddressSpace;
unsigned Struct_NumElements;
unsigned Argument_Info;
+ unsigned Argument_NumConstraints;
ElementCount Vector_Width;
};
@@ -231,6 +233,12 @@ namespace Intrinsic {
return Argument_Info & 0xFFFF;
}
+ // For ArgumentTypeConstraint: get number of allowed types.
+ unsigned getArgumentNumConstraints() const {
+ assert(Kind == ArgumentTypeConstraint);
+ return Argument_NumConstraints;
+ }
+
static IITDescriptor get(IITDescriptorKind K, unsigned Field) {
IITDescriptor Result = { K, { Field } };
return Result;
@@ -278,6 +286,10 @@ namespace Intrinsic {
LLVM_ABI bool matchIntrinsicVarArg(bool isVarArg,
ArrayRef<IITDescriptor> &Infos);
+ /// Verify type constraints for AnyTypeOf constrained intrinsics.
+ LLVM_ABI bool verifyIntrinsicTypeConstraints(ID id, FunctionType *FTy,
+ std::string &ErrMsg);
+
/// Gets the type arguments of an intrinsic call by matching type contraints
/// specified by the .td file. The overloaded types are pushed into the
/// AgTys vector.
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 35a4158a56da9..5ff061b255650 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -372,6 +372,8 @@ def IIT_V6 : IIT_Vec<6, 50>;
def IIT_V10 : IIT_Vec<10, 51>;
def IIT_V2048 : IIT_Vec<2048, 52>;
def IIT_V4096 : IIT_Vec<4096, 53>;
+// Constrained type encoding for overloaded intrinsics.
+def IIT_ANYTYPE : IIT_Base<93>;
}
defvar IIT_all_FixedTypes = !filter(iit, IIT_all,
@@ -448,6 +450,23 @@ class LLVMAnyPointerType : LLVMAnyType<pAny> {
assert isAny, "pAny should have isOverloaded";
}
+// AnyTypeOf: Constrain overloaded type to specific set of allowed types.
+// Encoding follows the pattern: [IIT_ARG, EncAny, IIT_ANYTYPE, count, type_sigs...].
+class AnyTypeOf<list<LLVMType> allowed_types> : LLVMAnyType<Any> {
+ list<LLVMType> AllowedTypes = allowed_types;
+
+ // Validation: specifying maximal 255 number of allowed types.
+ assert !le(!size(allowed_types), 255),
+ "AnyTypeOf cannot exceed 255 allowed types";
+
+ let Sig = !listconcat(
+ [IIT_ARG.Number, EncAnyType<ArgCode>.ret],
+ [IIT_ANYTYPE.Number, !size(allowed_types)],
+ !foldl([]<int>, allowed_types, accum, ty,
+ !listconcat(accum, ty.Sig))
+ );
+}
+
// Match the type of another intrinsic parameter. Number is an index into the
// list of overloaded types for the intrinsic, excluding all the fixed types.
// The Number value must refer to a previously listed type. For example:
diff --git a/llvm/include/llvm/IR/IntrinsicsNVVM.td b/llvm/include/llvm/IR/IntrinsicsNVVM.td
index aab85c2a86373..c7f154255050e 100644
--- a/llvm/include/llvm/IR/IntrinsicsNVVM.td
+++ b/llvm/include/llvm/IR/IntrinsicsNVVM.td
@@ -3312,4 +3312,16 @@ foreach sp = [0, 1] in {
}
}
+// Test intrinsic - TODO: Add the feature to existing intrinsics and remove these test intrinsics.
+// IIT_LongEncodingTable entry: /* 25339 */ 4, 15, 0, 93, 2, 3, 4, 15, 7, 15, 8, 93, 2, 15, 9, 7, 15, 16, 93, 2, 14, 24, 3, 15, 24, 93, 2, 5, 8, 15, 32, 93, 3, 3, 10, 4, 10, 7, 0,
+def int_nvvm_test_anytype : Intrinsic<[llvm_i32_ty],
+ [AnyTypeOf<[llvm_i16_ty, llvm_i32_ty]>, LLVMMatchType<0>, AnyTypeOf<[llvm_anyint_ty, llvm_float_ty]>, AnyTypeOf<[llvm_ptr_ty, llvm_shared_ptr_ty]>,
+ AnyTypeOf<[llvm_i64_ty, llvm_double_ty]>, AnyTypeOf<[llvm_i16_ty, llvm_v4i32_ty, llvm_v4f32_ty]>],
+ [IntrNoMem]>;
+
+// IIT_LongEncodingTable entry: /* 15435 */ 21, 1, 4, 15, 0, 93, 2, 3, 4, 5, 4, 0,
+def int_nvvm_test_return_type : Intrinsic<[llvm_i32_ty, AnyTypeOf<[llvm_i16_ty, llvm_i32_ty]>, llvm_i64_ty],
+[llvm_i32_ty],
+[IntrNoMem]>;
+
} // let TargetPrefix = "nvvm"
diff --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index f46d3e5063e43..88762923fd96d 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -353,8 +353,21 @@ DecodeIITType(unsigned &NextElt, ArrayRef<unsigned char> Infos,
case IIT_ARG: {
unsigned ArgInfo = (NextElt == Infos.size() ? 0 : Infos[NextElt++]);
OutputTable.push_back(IITDescriptor::get(IITDescriptor::Argument, ArgInfo));
+
+ if (NextElt < Infos.size() && Infos[NextElt] == IIT_ANYTYPE) {
+ NextElt++;
+
+ unsigned NumTypes = Infos[NextElt++];
+ OutputTable.push_back(IITDescriptor::get(IITDescriptor::ArgumentTypeConstraint, NumTypes));
+
+ for (unsigned i = 0; i < NumTypes; ++i)
+ DecodeIITType(NextElt, Infos, Info, OutputTable);
+ return;
+ }
return;
}
+ case IIT_ANYTYPE:
+ llvm_unreachable("IIT_ANYTYPE must follow IIT_ARG");
case IIT_EXTEND_ARG: {
unsigned ArgInfo = (NextElt == Infos.size() ? 0 : Infos[NextElt++]);
OutputTable.push_back(
@@ -570,6 +583,8 @@ static Type *DecodeFixedType(ArrayRef<Intrinsic::IITDescriptor> &Infos,
case IITDescriptor::VecOfAnyPtrsToElt:
// Return the overloaded type (which determines the pointers address space)
return Tys[D.getOverloadArgNumber()];
+ case IITDescriptor::ArgumentTypeConstraint:
+ llvm_unreachable("ArgumentTypeConstraint should not appear in DecodeFixedType");
}
llvm_unreachable("unhandled");
}
@@ -582,9 +597,26 @@ FunctionType *Intrinsic::getType(LLVMContext &Context, ID id,
ArrayRef<IITDescriptor> TableRef = Table;
Type *ResultTy = DecodeFixedType(TableRef, Tys, Context);
+ if (!TableRef.empty() && TableRef[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+ unsigned NumConstraints = TableRef[0].getArgumentNumConstraints();
+ TableRef = TableRef.slice(1);
+
+ for (unsigned i = 0; i < NumConstraints; ++i)
+ (void)DecodeFixedType(TableRef, Tys, Context);
+ }
+
SmallVector<Type *, 8> ArgTys;
- while (!TableRef.empty())
+ while (!TableRef.empty()) {
ArgTys.push_back(DecodeFixedType(TableRef, Tys, Context));
+
+ if (!TableRef.empty() && TableRef[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+ unsigned NumConstraints = TableRef[0].getArgumentNumConstraints();
+ TableRef = TableRef.slice(1);
+
+ for (unsigned i = 0; i < NumConstraints; ++i)
+ (void)DecodeFixedType(TableRef, Tys, Context);
+ }
+ }
// DecodeFixedType returns Void for IITDescriptor::Void and
// IITDescriptor::VarArg If we see void type as the type of the last argument,
@@ -833,6 +865,50 @@ bool Intrinsic::hasConstrainedFPRoundingModeOperand(Intrinsic::ID QID) {
}
}
+// Helper to skip past descriptors for one complete type in AnyTypeOf constraints.
+static unsigned skipDescriptorsForSingleType(ArrayRef<Intrinsic::IITDescriptor> &Infos) {
+ using namespace Intrinsic;
+
+ if (Infos.empty())
+ return 0;
+
+ IITDescriptor D = Infos[0];
+ unsigned Count = 1;
+ Infos = Infos.slice(1);
+
+ switch (D.Kind) {
+ case IITDescriptor::Vector:
+ Count += skipDescriptorsForSingleType(Infos);
+ break;
+
+ case IITDescriptor::Pointer:
+ break;
+
+ case IITDescriptor::Struct:
+ for (unsigned i = 0, e = D.Struct_NumElements; i != e; ++i)
+ Count += skipDescriptorsForSingleType(Infos);
+ break;
+
+ case IITDescriptor::SameVecWidthArgument:
+ Count += skipDescriptorsForSingleType(Infos);
+ break;
+
+ case IITDescriptor::Argument:
+ if (!Infos.empty() && Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+ unsigned NumConstraints = Infos[0].getArgumentNumConstraints();
+ Count++;
+ Infos = Infos.slice(1);
+ for (unsigned i = 0; i < NumConstraints; ++i)
+ Count += skipDescriptorsForSingleType(Infos);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return Count;
+}
+
using DeferredIntrinsicMatchPair =
std::pair<Type *, ArrayRef<Intrinsic::IITDescriptor>>;
@@ -919,6 +995,29 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
// verify that the later instance matches the previous instance.
if (D.getArgumentNumber() < ArgTys.size())
return Ty != ArgTys[D.getArgumentNumber()];
+
+ switch (D.getArgumentKind()) {
+ case IITDescriptor::AK_Any:
+ break;
+ case IITDescriptor::AK_AnyInteger:
+ if (!Ty->isIntOrIntVectorTy())
+ return true;
+ break;
+ case IITDescriptor::AK_AnyFloat:
+ if (!Ty->isFPOrFPVectorTy())
+ return true;
+ break;
+ case IITDescriptor::AK_AnyVector:
+ if (!isa<VectorType>(Ty))
+ return true;
+ break;
+ case IITDescriptor::AK_AnyPointer:
+ if (!isa<PointerType>(Ty))
+ return true;
+ break;
+ case IITDescriptor::AK_MatchType:
+ break;
+ }
if (D.getArgumentNumber() > ArgTys.size() ||
D.getArgumentKind() == IITDescriptor::AK_MatchType)
@@ -927,22 +1026,18 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
assert(D.getArgumentNumber() == ArgTys.size() && !IsDeferredCheck &&
"Table consistency error");
ArgTys.push_back(Ty);
-
- switch (D.getArgumentKind()) {
- case IITDescriptor::AK_Any:
- return false; // Success
- case IITDescriptor::AK_AnyInteger:
- return !Ty->isIntOrIntVectorTy();
- case IITDescriptor::AK_AnyFloat:
- return !Ty->isFPOrFPVectorTy();
- case IITDescriptor::AK_AnyVector:
- return !isa<VectorType>(Ty);
- case IITDescriptor::AK_AnyPointer:
- return !isa<PointerType>(Ty);
- default:
- break;
+
+ if (!Infos.empty() && Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+ unsigned NumConstraints = Infos[0].getArgumentNumConstraints();
+ Infos = Infos.slice(1);
+
+ for (unsigned i = 0; i < NumConstraints; ++i)
+ skipDescriptorsForSingleType(Infos);
+
+ return false;
}
- llvm_unreachable("all argument kinds not covered");
+
+ return false;
case IITDescriptor::ExtendArgument: {
// If this is a forward reference, defer the check for later.
@@ -1058,6 +1153,8 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
return true;
return ThisArgVecTy != VectorType::getInteger(ReferenceType);
}
+ case IITDescriptor::ArgumentTypeConstraint:
+ llvm_unreachable("ArgumentTypeConstraint should be handled in Argument case");
}
llvm_unreachable("unhandled");
}
@@ -1088,6 +1185,174 @@ Intrinsic::matchIntrinsicSignature(FunctionType *FTy,
return MatchIntrinsicTypes_Match;
}
+// Helper: Check if a type matches AnyTypeOf constraints using matchIntrinsicType.
+static bool verifyTypeAgainstConstraints(
+ Type *Ty, unsigned NumConstraints,
+ ArrayRef<Intrinsic::IITDescriptor> &Infos) {
+ using namespace Intrinsic;
+
+ bool Matched = false;
+ for (unsigned i = 0; i < NumConstraints && !Matched; ++i) {
+ ArrayRef<IITDescriptor> TypeDesc = Infos;
+ SmallVector<Type *, 4> DummyArgTys;
+ SmallVector<DeferredIntrinsicMatchPair, 2> DummyDeferredChecks;
+
+ if (!matchIntrinsicType(Ty, TypeDesc, DummyArgTys, DummyDeferredChecks, false)) {
+ Matched = true;
+ for (unsigned j = 0; j < NumConstraints - i; ++j)
+ skipDescriptorsForSingleType(Infos);
+ break;
+ }
+ skipDescriptorsForSingleType(Infos);
+ }
+
+ return Matched;
+}
+
+// Helper: Format a type as string for error messages.
+static std::string typeToString(Type *Ty) {
+ std::string Str;
+ raw_string_ostream OS(Str);
+ Ty->print(OS);
+ return Str;
+}
+
+bool Intrinsic::verifyIntrinsicTypeConstraints(
+ ID id, FunctionType *FTy,
+ std::string &ErrMsg) {
+
+ if (id == 0 || id >= Intrinsic::num_intrinsics)
+ return true;
+
+ SmallVector<IITDescriptor, 8> Table;
+ getIntrinsicInfoTableEntries(id, Table);
+
+ if (Table.empty())
+ return true;
+
+ ArrayRef<IITDescriptor> Infos = Table;
+ SmallVector<Type *, 4> ArgTys;
+
+ // Processing return type.
+ Type *RetTy = FTy->getReturnType();
+ if (!Infos.empty() && Infos[0].Kind == IITDescriptor::Argument) {
+ Infos = Infos.slice(1);
+
+ if (!Infos.empty() && Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+ unsigned NumConstraints = Infos[0].getArgumentNumConstraints();
+ Infos = Infos.slice(1);
+
+ bool Matched = false;
+
+ // Check if a struct type's elements are all present in AnyTypeOf constraint list.
+ if (auto *STy = dyn_cast<StructType>(RetTy)) {
+ SmallVector<Type *, 8> AllowedTypes;
+ ArrayRef<IITDescriptor> TempInfos = Infos;
+ for (unsigned i = 0; i < NumConstraints; ++i) {
+ ArrayRef<IITDescriptor> TypeDesc = TempInfos;
+ SmallVector<Type *, 4> DummyTys;
+ Type *ConstraintTy = DecodeFixedType(TypeDesc, DummyTys, FTy->getContext());
+ AllowedTypes.push_back(ConstraintTy);
+ TempInfos = TypeDesc;
+ }
+
+ Matched = llvm::all_of(STy->elements(), [&](Type *ElemTy) {
+ return llvm::is_contained(AllowedTypes, ElemTy);
+ });
+
+ if (Matched) {
+ for (unsigned i = 0; i < NumConstraints; ++i)
+ skipDescriptorsForSingleType(Infos);
+ Matched = true;
+ }
+ }
+
+ if (!Matched) {
+ for (unsigned i = 0; i < NumConstraints && !Matched; ++i) {
+ ArrayRef<IITDescriptor> TypeDesc = Infos;
+ SmallVector<Type *, 4> DummyArgTys;
+ SmallVector<DeferredIntrinsicMatchPair, 2> DummyDeferredChecks;
+
+ if (!matchIntrinsicType(RetTy, TypeDesc, DummyArgTys,
+ DummyDeferredChecks, false)) {
+ Matched = true;
+ for (unsigned j = 0; j < NumConstraints - i; ++j)
+ skipDescriptorsForSingleType(Infos);
+ break;
+ }
+ skipDescriptorsForSingleType(Infos);
+ }
+ }
+
+ if (!Matched) {
+ ErrMsg = "Return type '" + typeToString(RetTy) + "' not in allowed types";
+ return false;
+ }
+ }
+ } else if (!Infos.empty() && Infos[0].Kind == IITDescriptor::Struct) {
+ // Handle struct return type with AnyTypeOf constrained elements.
+ auto *STy = dyn_cast<StructType>(RetTy);
+ if (!STy)
+ skipDescriptorsForSingleType(Infos);
+ else {
+ unsigned NumElements = Infos[0].Struct_NumElements;
+ Infos = Infos.slice(1);
+
+ for (unsigned ElemIdx = 0; ElemIdx < NumElements; ++ElemIdx) {
+ if (Infos.empty())
+ break;
+
+ if (Infos[0].Kind == IITDescriptor::Argument) {
+ Infos = Infos.slice(1);
+
+ if (!Infos.empty() && Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+ unsigned NumConstraints = Infos[0].getArgumentNumConstraints();
+ Infos = Infos.slice(1);
+
+ Type *ElemTy = STy->getElementType(ElemIdx);
+ if (!verifyTypeAgainstConstraints(ElemTy, NumConstraints, Infos)) {
+ ErrMsg = "Return type struct element " + std::to_string(ElemIdx) +
+ " type '" + typeToString(ElemTy) + "' not in allowed types";
+ return false;
+ }
+ }
+ } else
+ skipDescriptorsForSingleType(Infos);
+ }
+ }
+ } else if (!Infos.empty())
+ skipDescriptorsForSingleType(Infos);
+
+ // Processing parameters.
+ for (unsigned ParamIdx = 0; ParamIdx < FTy->getNumParams(); ++ParamIdx) {
+ if (Infos.empty())
+ break;
+
+ Type *ParamTy = FTy->getParamType(ParamIdx);
+
+ if (Infos[0].Kind == IITDescriptor::Argument) {
+ unsigned ArgNum = Infos[0].getArgumentNumber();
+ Infos = Infos.slice(1);
+
+ if (!Infos.empty() && Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+ unsigned NumConstraints = Infos[0].getArgumentNumConstraints();
+ Infos = Infos.slice(1);
+
+ if (!verifyTypeAgainstConstraints(ParamTy, NumConstraints, Infos)) {
+ ErrMsg = "Parameter " + std::to_string(ParamIdx) + " type '" +
+ typeToString(ParamTy) + "' not in allowed types";
+ return false;
+ }
+
+ if (ArgNum == ArgTys.size())
+ ArgTys.push_back(ParamTy);
+ }
+ } else
+ skipDescriptorsForSingleType(Infos);
+ }
+ return true;
+}
+
bool Intrinsic::matchIntrinsicVarArg(
bool isVarArg, ArrayRef<Intrinsic::IITDescriptor> &Infos) {
// If there are no descriptors left, then it can't be a vararg.
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 543c26dfe25e0..ec771ff773223 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -3274,6 +3274,16 @@ void Verifier::visitFunction(const Function &F) {
}
}
+ // Verify AnyTypeOf type constraints for intrinsic declarations.
+ if (F.isIntrinsic()) {
+ std::string ConstraintErrMsg;
+ if (!Intrinsic::verifyIntrinsicTypeConstraints(F.getIntrinsicID(),
+ F.getFunctionType(),
+ ConstraintErrMsg))
+ CheckFailed("Intrinsic declaration '" + F.getName().str() +
+ "' violates type constraint: " + ConstraintErrMsg);
+ }
+
auto *N = F.getSubprogram();
HasDebugInfo = (N != nullptr);
if (!HasDebugInfo)
diff --git a/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll
new file mode 100644
index 0000000000000..7647a04898299
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll
@@ -0,0 +1,51 @@
+; RUN: not llvm-as < %s -o /dev/null
+; Test cases for int_nvvm_test_anytype intrinsic - INVALID combinations
+
+; arg0 must be i16 or i32, not i8
+define i32 @invalid_arg0_i8(i8 %a0, i8 %a1, i32 %a2, ptr %a3, i64 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i8.i8.i32.p0.i64.i16(i8 %a0, i8 %a1, i32 %a2, ptr %a3, i64 %a4, i16 %a5)
+ ret i32 %result
+}
+
+; arg1 must match arg0 (i16), not i32
+define i32 @invalid_arg1_mismatch_i16_i32(i16 %a0, i32 %a1, i32 %a2, ptr %a3, i64 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i16.i32.i32.p0.i64.i16(i16 %a0, i32 %a1, i32 %a2, ptr %a3, i64 %a4, i16 %a5)
+ ret i32 %result
+}
+
+; arg2 must be anyint or float, not double
+define i32 @invalid_arg2_double(i32 %a0, i32 %a1, double %a2, ptr %a3, i64 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i32.i32.f64.p0.i64.i16(i32 %a0, i32 %a1, double %a2, ptr %a3, i64 %a4, i16 %a5)
+ ret i32 %result
+}
+
+; arg3 must be ptr or ptr addrspace(3), not ptr addrspace(1) (global)
+define i32 @invalid_arg3_ptr_as1_wrong(i32 %a0, i32 %a1, i32 %a2, ptr addrspace(1) %a3, i64 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i32.i32.i32.p1.i64.i16(i32 %a0, i32 %a1, i32 %a2, ptr addrspace(1) %a3, i64 %a4, i16 %a5)
+ ret i32 %result
+}
+
+; arg4 must be i64 or double, not i32
+define i32 @invalid_arg4_i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.i32.i16(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, i16 %a5)
+ ret i32 %result
+}
+
+; arg5 must be i16, <4 x i32>, or <4 x f32>, not <2 x i32>
+define i32 @invalid_arg5_v2i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i64 %a4, <2 x i32> %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.i64.v2i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i64 %a4, <2 x i32> %a5)
+ ret i32 %result
+}
+
+define {i32, i8, i64} @invalid_return_type_i8(i32 %arg) {
+ %result = call {i32, i8, i64} @llvm.nvvm.test.return.type.i8(i32 %arg)
+ ret {i32, i8, i64} %result
+}
+
+declare i32 @llvm.nvvm.test.anytype.i8.i8.i32.p0.i64.i16(i8, i8, i32, ptr, i64, i16)
+declare i32 @llvm.nvvm.test.anytype.i16.i32.i32.p0.i64.i16(i16, i32, i32, ptr, i64, i16)
+declare i32 @llvm.nvvm.test.anytype.i32.i32.f64.p0.i64.i16(i32, i32, double, ptr, i64, i16)
+declare i32 @llvm.nvvm.test.anytype.i32.i32.i32.p1.i64.i16(i32, i32, i32, ptr addrspace(1), i64, i16)
+declare i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.i32.i16(i32, i32, i32, ptr, i32, i16)
+declare i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.i64.v2i32(i32, i32, i32, ptr, i64, <2 x i32>)
+declare {i32, i8, i64} @llvm.nvvm.test.return.type.i8(i32)
diff --git a/llvm/test/CodeGen/NVPTX/anytypeof_constraints_valid.ll b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_valid.ll
new file mode 100644
index 0000000000000..2efa46c04116f
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_valid.ll
@@ -0,0 +1,45 @@
+; RUN: llvm-as < %s -o /dev/null
+; Test cases for int_nvvm_test_anytype intrinsic - VALID combinations
+
+define i32 @test_arg0_i16(i16 %a0, i16 %a1, i32 %a2, ptr %a3, i64 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i16.i16.i32.p0.i64.i16(i16 %a0, i16 %a1, i32 %a2, ptr %a3, i64 %a4, i16 %a5)
+ ret i32 %result
+}
+
+define i32 @test_arg0_i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i64 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.i64.i16(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i64 %a4, i16 %a5)
+ ret i32 %result
+}
+
+define i32 @test_arg2_anyint(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i64 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.i64.i16(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i64 %a4, i16 %a5)
+ ret i32 %result
+}
+
+define i32 @test_arg3_shared_ptr(i32 %a0, i32 %a1, i32 %a2, ptr addrspace(3) %a3, i64 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i32.i32.i32.p3.i64.i16(i32 %a0, i32 %a1, i32 %a2, ptr addrspace(3) %a3, i64 %a4, i16 %a5)
+ ret i32 %result
+}
+
+define i32 @test_arg4_double(i32 %a0, i32 %a1, i32 %a2, ptr %a3, double %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.f64.i16(i32 %a0, i32 %a1, i32 %a2, ptr %a3, double %a4, i16 %a5)
+ ret i32 %result
+}
+
+define i32 @test_arg5_v4i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i64 %a4, <4 x i32> %a5) {
+ %result = call i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.i64.v4i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i64 %a4, <4 x i32> %a5)
+ ret i32 %result
+}
+
+define {i32, i32, i64} @test_return_type_valid(i32 %arg) {
+ %result = call {i32, i32, i64} @llvm.nvvm.test.return.type.i32(i32 %arg)
+ ret {i32, i32, i64} %result
+}
+
+declare i32 @llvm.nvvm.test.anytype.i16.i16.i32.p0.i64.i16(i16, i16, i32, ptr, i64, i16)
+declare i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.i64.i16(i32, i32, i32, ptr, i64, i16)
+declare i32 @llvm.nvvm.test.anytype.i32.i32.f32.p0.i64.i16(i32, i32, i32, ptr, i64, i16)
+declare i32 @llvm.nvvm.test.anytype.i32.i32.i32.p3.i64.i16(i32, i32, i32, ptr addrspace(3), i64, i16)
+declare i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.f64.i16(i32, i32, i32, ptr, double, i16)
+declare i32 @llvm.nvvm.test.anytype.i32.i32.i32.p0.i64.v4i32(i32, i32, i32, ptr, i64, <4 x i32>)
+declare {i32, i32, i64} @llvm.nvvm.test.return.type.i32(i32)
More information about the llvm-commits
mailing list