[llvm] [LLVM-Tablegen] Except predicate for overloaded intrinsics with constraints (PR #175445)
Dharuni R Acharya via llvm-commits
llvm-commits at lists.llvm.org
Sun Jan 11 08:54:41 PST 2026
https://github.com/DharuniRAcharya created https://github.com/llvm/llvm-project/pull/175445
Depends on #172442.
This patch introduces an `Except` constraint for overloaded intrinsics to exclude specific type
combinations produced by `AnyTypeOf/NoneTypeOf` products.
This PR builds on the explicit type constraint infrastructure introduced in PR #172442.
The first 4 commits are from the base PR; only the final commit adds the Except predicate.
Link to the RFC, where this feature was discussed:
https://discourse.llvm.org/t/rfc-tablegen-explicit-type-constraints-for-overloaded-llvm-intrinsics/89142
>From 425a0fe893e65ae9bc88d5094643b811400e2776 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 1/4] [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/lib/IR/Intrinsics.cpp | 297 +++++++++++++++++-
llvm/lib/IR/Verifier.cpp | 10 +
.../NVPTX/anytypeof_constraints_invalid.ll | 51 +++
.../NVPTX/anytypeof_constraints_valid.ll | 45 +++
6 files changed, 418 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 c56b0185b4f1e..028b65c267ab7 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/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 a2ad95eb5abc4..963c01b5a1db0 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -3290,6 +3290,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)
>From 831b5b4f799ebf8720d22db8cb7c9e44e1179fea Mon Sep 17 00:00:00 2001
From: Dharuni R Acharya <dharunira at nvidia.com>
Date: Wed, 17 Dec 2025 05:03:56 +0000
Subject: [PATCH 2/4] Fix formatting & Resolve conflicts
---
llvm/include/llvm/IR/Intrinsics.h | 3 +-
llvm/include/llvm/IR/IntrinsicsNVVM.td | 12 ++
llvm/lib/IR/Intrinsics.cpp | 155 ++++++++++++++-----------
llvm/lib/IR/Verifier.cpp | 5 +-
4 files changed, 102 insertions(+), 73 deletions(-)
diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h
index 3d2dcd5e0da1a..0b1daa102c2a2 100644
--- a/llvm/include/llvm/IR/Intrinsics.h
+++ b/llvm/include/llvm/IR/Intrinsics.h
@@ -181,7 +181,8 @@ namespace Intrinsic {
AMX,
PPCQuad,
AArch64Svcount,
- ArgumentTypeConstraint, // For AnyTypeOf - marks constrained argument types.
+ ArgumentTypeConstraint, // For AnyTypeOf - marks constrained argument
+ // types.
} Kind;
union {
diff --git a/llvm/include/llvm/IR/IntrinsicsNVVM.td b/llvm/include/llvm/IR/IntrinsicsNVVM.td
index bad655c7c4d1c..bb8a8c7caf284 100644
--- a/llvm/include/llvm/IR/IntrinsicsNVVM.td
+++ b/llvm/include/llvm/IR/IntrinsicsNVVM.td
@@ -3386,4 +3386,16 @@ def int_nvvm_tensormap_replace_fill_mode :
ArgInfo<ArgIndex<1>, [ArgName<"fill_mode">,
ImmArgPrinter<"printTensormapFillMode">]>]>;
+// 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 88762923fd96d..0e349925b2c8c 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -358,8 +358,9 @@ DecodeIITType(unsigned &NextElt, ArrayRef<unsigned char> Infos,
NextElt++;
unsigned NumTypes = Infos[NextElt++];
- OutputTable.push_back(IITDescriptor::get(IITDescriptor::ArgumentTypeConstraint, NumTypes));
-
+ OutputTable.push_back(
+ IITDescriptor::get(IITDescriptor::ArgumentTypeConstraint, NumTypes));
+
for (unsigned i = 0; i < NumTypes; ++i)
DecodeIITType(NextElt, Infos, Info, OutputTable);
return;
@@ -584,7 +585,8 @@ static Type *DecodeFixedType(ArrayRef<Intrinsic::IITDescriptor> &Infos,
// 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(
+ "ArgumentTypeConstraint should not appear in DecodeFixedType");
}
llvm_unreachable("unhandled");
}
@@ -597,10 +599,11 @@ 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) {
+ 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);
}
@@ -608,11 +611,12 @@ FunctionType *Intrinsic::getType(LLVMContext &Context, ID id,
SmallVector<Type *, 8> ArgTys;
while (!TableRef.empty()) {
ArgTys.push_back(DecodeFixedType(TableRef, Tys, Context));
-
- if (!TableRef.empty() && TableRef[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+
+ 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);
}
@@ -865,36 +869,39 @@ 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) {
+// 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) {
+ if (!Infos.empty() &&
+ Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
unsigned NumConstraints = Infos[0].getArgumentNumConstraints();
Count++;
Infos = Infos.slice(1);
@@ -995,7 +1002,7 @@ 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;
@@ -1026,14 +1033,15 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
assert(D.getArgumentNumber() == ArgTys.size() && !IsDeferredCheck &&
"Table consistency error");
ArgTys.push_back(Ty);
-
- if (!Infos.empty() && Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+
+ if (!Infos.empty() &&
+ Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
unsigned NumConstraints = Infos[0].getArgumentNumConstraints();
- Infos = Infos.slice(1);
+ Infos = Infos.slice(1);
for (unsigned i = 0; i < NumConstraints; ++i)
skipDescriptorsForSingleType(Infos);
-
+
return false;
}
@@ -1154,7 +1162,8 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
return ThisArgVecTy != VectorType::getInteger(ReferenceType);
}
case IITDescriptor::ArgumentTypeConstraint:
- llvm_unreachable("ArgumentTypeConstraint should be handled in Argument case");
+ llvm_unreachable(
+ "ArgumentTypeConstraint should be handled in Argument case");
}
llvm_unreachable("unhandled");
}
@@ -1185,19 +1194,21 @@ 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) {
+// 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)) {
+
+ if (!matchIntrinsicType(Ty, TypeDesc, DummyArgTys, DummyDeferredChecks,
+ false)) {
Matched = true;
for (unsigned j = 0; j < NumConstraints - i; ++j)
skipDescriptorsForSingleType(Infos);
@@ -1205,7 +1216,7 @@ static bool verifyTypeAgainstConstraints(
}
skipDescriptorsForSingleType(Infos);
}
-
+
return Matched;
}
@@ -1217,63 +1228,65 @@ static std::string typeToString(Type *Ty) {
return Str;
}
-bool Intrinsic::verifyIntrinsicTypeConstraints(
- ID id, FunctionType *FTy,
- std::string &ErrMsg) {
-
+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) {
+
+ 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.
+
+ // 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());
+ 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);
+ 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,
+
+ if (!matchIntrinsicType(RetTy, TypeDesc, DummyArgTys,
DummyDeferredChecks, false)) {
Matched = true;
for (unsigned j = 0; j < NumConstraints - i; ++j)
@@ -1283,9 +1296,10 @@ bool Intrinsic::verifyIntrinsicTypeConstraints(
skipDescriptorsForSingleType(Infos);
}
}
-
+
if (!Matched) {
- ErrMsg = "Return type '" + typeToString(RetTy) + "' not in allowed types";
+ ErrMsg =
+ "Return type '" + typeToString(RetTy) + "' not in allowed types";
return false;
}
}
@@ -1297,22 +1311,24 @@ bool Intrinsic::verifyIntrinsicTypeConstraints(
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) {
+
+ 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";
+ ErrMsg = "Return type struct element " + std::to_string(ElemIdx) +
+ " type '" + typeToString(ElemTy) +
+ "' not in allowed types";
return false;
}
}
@@ -1327,26 +1343,27 @@ bool Intrinsic::verifyIntrinsicTypeConstraints(
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) {
+
+ 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 '" +
+ 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);
}
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 963c01b5a1db0..301a57b680d7b 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -3293,9 +3293,8 @@ 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))
+ if (!Intrinsic::verifyIntrinsicTypeConstraints(
+ F.getIntrinsicID(), F.getFunctionType(), ConstraintErrMsg))
CheckFailed("Intrinsic declaration '" + F.getName().str() +
"' violates type constraint: " + ConstraintErrMsg);
}
>From 0a037107ffcc7d0162651ffc75ef777032bb0942 Mon Sep 17 00:00:00 2001
From: Dharuni R Acharya <dharuniracharya at gmail.com>
Date: Sat, 10 Jan 2026 16:37:10 +0000
Subject: [PATCH 3/4] Addition of NoneTypeOf Implementation
---
llvm/include/llvm/IR/Intrinsics.h | 7 +
llvm/include/llvm/IR/Intrinsics.td | 18 +
llvm/include/llvm/IR/IntrinsicsNVVM.td | 9 +-
llvm/lib/IR/Intrinsics.cpp | 336 ++++++++++++------
.../NVPTX/anytypeof_constraints_invalid.ll | 40 +--
.../NVPTX/anytypeof_constraints_valid.ll | 38 +-
.../overloaded-intrinsic-constraints.td | 43 +++
7 files changed, 337 insertions(+), 154 deletions(-)
create mode 100644 llvm/test/TableGen/overloaded-intrinsic-constraints.td
diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h
index 0b1daa102c2a2..dd782a6b5b895 100644
--- a/llvm/include/llvm/IR/Intrinsics.h
+++ b/llvm/include/llvm/IR/Intrinsics.h
@@ -183,6 +183,7 @@ namespace Intrinsic {
AArch64Svcount,
ArgumentTypeConstraint, // For AnyTypeOf - marks constrained argument
// types.
+ ArgumentTypeExclusion, // For NoneTypeOf - marks excluded argument types.
} Kind;
union {
@@ -240,6 +241,12 @@ namespace Intrinsic {
return Argument_NumConstraints;
}
+ // For ArgumentTypeExclusion: get number of excluded types.
+ unsigned getArgumentNumExclusions() const {
+ assert(Kind == ArgumentTypeExclusion);
+ return Argument_NumConstraints;
+ }
+
static IITDescriptor get(IITDescriptorKind K, unsigned Field) {
IITDescriptor Result = { K, { Field } };
return Result;
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 028b65c267ab7..0f02a77b5323e 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -374,6 +374,7 @@ 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>;
+def IIT_NONETYPE : IIT_Base<94>;
}
defvar IIT_all_FixedTypes = !filter(iit, IIT_all,
@@ -467,6 +468,23 @@ class AnyTypeOf<list<LLVMType> allowed_types> : LLVMAnyType<Any> {
);
}
+// NoneTypeOf: Exclude specific types from overloaded type (inverse of AnyTypeOf).
+// Encoding follows the pattern: [IIT_ARG, EncAny, IIT_NONETYPE, count, type_sigs...].
+class NoneTypeOf<list<LLVMType> excluded_types> : LLVMAnyType<Any> {
+ list<LLVMType> ExcludedTypes = excluded_types;
+
+ // Validation: specifying maximal 255 number of excluded types.
+ assert !le(!size(excluded_types), 255),
+ "NoneTypeOf cannot exceed 255 excluded types";
+
+ let Sig = !listconcat(
+ [IIT_ARG.Number, EncAnyType<ArgCode>.ret],
+ [IIT_NONETYPE.Number, !size(excluded_types)],
+ !foldl([]<int>, excluded_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 232a5e89f5941..d0bb6b5232071 100644
--- a/llvm/include/llvm/IR/IntrinsicsNVVM.td
+++ b/llvm/include/llvm/IR/IntrinsicsNVVM.td
@@ -3387,11 +3387,10 @@ def int_nvvm_tensormap_replace_fill_mode :
ImmArgPrinter<"printTensormapFillMode">]>]>;
// 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: /* 16662 */ 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, 94, 2, 5, 8, 15, 32, 93, 3, 3, 10, 4, 10, 7, 0,
+def int_nvvm_test_type : 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]>, NoneTypeOf<[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],
diff --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index 0e349925b2c8c..91720067e3a41 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -365,10 +365,24 @@ DecodeIITType(unsigned &NextElt, ArrayRef<unsigned char> Infos,
DecodeIITType(NextElt, Infos, Info, OutputTable);
return;
}
+
+ if (NextElt < Infos.size() && Infos[NextElt] == IIT_NONETYPE) {
+ NextElt++;
+
+ unsigned NumTypes = Infos[NextElt++];
+ OutputTable.push_back(
+ IITDescriptor::get(IITDescriptor::ArgumentTypeExclusion, 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_NONETYPE:
+ llvm_unreachable("IIT_NONETYPE must follow IIT_ARG");
case IIT_EXTEND_ARG: {
unsigned ArgInfo = (NextElt == Infos.size() ? 0 : Infos[NextElt++]);
OutputTable.push_back(
@@ -587,6 +601,9 @@ static Type *DecodeFixedType(ArrayRef<Intrinsic::IITDescriptor> &Infos,
case IITDescriptor::ArgumentTypeConstraint:
llvm_unreachable(
"ArgumentTypeConstraint should not appear in DecodeFixedType");
+ case IITDescriptor::ArgumentTypeExclusion:
+ llvm_unreachable(
+ "ArgumentTypeExclusion should not appear in DecodeFixedType");
}
llvm_unreachable("unhandled");
}
@@ -608,6 +625,15 @@ FunctionType *Intrinsic::getType(LLVMContext &Context, ID id,
(void)DecodeFixedType(TableRef, Tys, Context);
}
+ if (!TableRef.empty() &&
+ TableRef[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
+ unsigned NumExclusions = TableRef[0].getArgumentNumExclusions();
+ TableRef = TableRef.slice(1);
+
+ for (unsigned i = 0; i < NumExclusions; ++i)
+ (void)DecodeFixedType(TableRef, Tys, Context);
+ }
+
SmallVector<Type *, 8> ArgTys;
while (!TableRef.empty()) {
ArgTys.push_back(DecodeFixedType(TableRef, Tys, Context));
@@ -620,6 +646,15 @@ FunctionType *Intrinsic::getType(LLVMContext &Context, ID id,
for (unsigned i = 0; i < NumConstraints; ++i)
(void)DecodeFixedType(TableRef, Tys, Context);
}
+
+ if (!TableRef.empty() &&
+ TableRef[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
+ unsigned NumExclusions = TableRef[0].getArgumentNumExclusions();
+ TableRef = TableRef.slice(1);
+
+ for (unsigned i = 0; i < NumExclusions; ++i)
+ (void)DecodeFixedType(TableRef, Tys, Context);
+ }
}
// DecodeFixedType returns Void for IITDescriptor::Void and
@@ -908,6 +943,14 @@ skipDescriptorsForSingleType(ArrayRef<Intrinsic::IITDescriptor> &Infos) {
for (unsigned i = 0; i < NumConstraints; ++i)
Count += skipDescriptorsForSingleType(Infos);
}
+ if (!Infos.empty() &&
+ Infos[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
+ unsigned NumExclusions = Infos[0].getArgumentNumExclusions();
+ Count++;
+ Infos = Infos.slice(1);
+ for (unsigned i = 0; i < NumExclusions; ++i)
+ Count += skipDescriptorsForSingleType(Infos);
+ }
break;
default:
@@ -1045,6 +1088,17 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
return false;
}
+ if (!Infos.empty() &&
+ Infos[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
+ unsigned NumExclusions = Infos[0].getArgumentNumExclusions();
+ Infos = Infos.slice(1);
+
+ for (unsigned i = 0; i < NumExclusions; ++i)
+ skipDescriptorsForSingleType(Infos);
+
+ return false;
+ }
+
return false;
case IITDescriptor::ExtendArgument: {
@@ -1164,6 +1218,9 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
case IITDescriptor::ArgumentTypeConstraint:
llvm_unreachable(
"ArgumentTypeConstraint should be handled in Argument case");
+ case IITDescriptor::ArgumentTypeExclusion:
+ llvm_unreachable(
+ "ArgumentTypeExclusion should be handled in Argument case");
}
llvm_unreachable("unhandled");
}
@@ -1194,7 +1251,7 @@ Intrinsic::matchIntrinsicSignature(FunctionType *FTy,
return MatchIntrinsicTypes_Match;
}
-// Helper: Check if a type matches AnyTypeOf constraints using
+// Check if a type matches AnyTypeOf constraints using
// matchIntrinsicType.
static bool
verifyTypeAgainstConstraints(Type *Ty, unsigned NumConstraints,
@@ -1204,10 +1261,10 @@ verifyTypeAgainstConstraints(Type *Ty, unsigned NumConstraints,
bool Matched = false;
for (unsigned i = 0; i < NumConstraints && !Matched; ++i) {
ArrayRef<IITDescriptor> TypeDesc = Infos;
- SmallVector<Type *, 4> DummyArgTys;
- SmallVector<DeferredIntrinsicMatchPair, 2> DummyDeferredChecks;
+ SmallVector<Type *, 4> TempArgTys;
+ SmallVector<DeferredIntrinsicMatchPair, 2> TempDeferredChecks;
- if (!matchIntrinsicType(Ty, TypeDesc, DummyArgTys, DummyDeferredChecks,
+ if (!matchIntrinsicType(Ty, TypeDesc, TempArgTys, TempDeferredChecks,
false)) {
Matched = true;
for (unsigned j = 0; j < NumConstraints - i; ++j)
@@ -1220,7 +1277,7 @@ verifyTypeAgainstConstraints(Type *Ty, unsigned NumConstraints,
return Matched;
}
-// Helper: Format a type as string for error messages.
+// Format a type as string for error messages.
static std::string typeToString(Type *Ty) {
std::string Str;
raw_string_ostream OS(Str);
@@ -1228,118 +1285,137 @@ static std::string typeToString(Type *Ty) {
return Str;
}
-bool Intrinsic::verifyIntrinsicTypeConstraints(ID id, FunctionType *FTy,
- std::string &ErrMsg) {
+enum class ConstraintKind {
+ Allowed,
+ Excluded,
+};
- if (id == 0 || id >= Intrinsic::num_intrinsics)
- return true;
+static bool
+checkAndConsumeConstraintBlock(Type *Ty, ConstraintKind Kind,
+ ArrayRef<Intrinsic::IITDescriptor> &Infos,
+ const Twine &Constraint, std::string &ErrMsg) {
+ using namespace Intrinsic;
- SmallVector<IITDescriptor, 8> Table;
- getIntrinsicInfoTableEntries(id, Table);
+ unsigned Count = Kind == ConstraintKind::Allowed
+ ? Infos.front().getArgumentNumConstraints()
+ : Infos.front().getArgumentNumExclusions();
+ Infos = Infos.slice(1);
- if (Table.empty())
+ bool Matches = verifyTypeAgainstConstraints(Ty, Count, Infos);
+ bool Violates = Kind == ConstraintKind::Allowed ? !Matches : Matches;
+ if (!Violates)
return true;
- ArrayRef<IITDescriptor> Infos = Table;
- SmallVector<Type *, 4> ArgTys;
+ ErrMsg = (Constraint + " type '" + typeToString(Ty) + "'" +
+ (Kind == ConstraintKind::Allowed ? " not in allowed types"
+ : " is in excluded types"))
+ .str();
+ return false;
+}
- // Processing return type.
- Type *RetTy = FTy->getReturnType();
- if (!Infos.empty() && Infos[0].Kind == IITDescriptor::Argument) {
- Infos = Infos.slice(1);
+// Verify the Struct return types.
+static bool
+verifyIntrinsicStructOutputTypes(Type *RetTy,
+ ArrayRef<Intrinsic::IITDescriptor> &Infos,
+ std::string &ErrMsg) {
+ using namespace Intrinsic;
- if (!Infos.empty() &&
- Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
- unsigned NumConstraints = Infos[0].getArgumentNumConstraints();
- Infos = Infos.slice(1);
+ auto *STy = dyn_cast<StructType>(RetTy);
+ if (!STy) {
+ skipDescriptorsForSingleType(Infos);
+ return false;
+ }
- 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 (Infos.empty() || Infos[0].Kind != IITDescriptor::Struct) {
+ ErrMsg = "Return type struct encoding is malformed";
+ return false;
+ }
- 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);
- }
- }
+ unsigned NumElements = Infos[0].Struct_NumElements;
+ Infos = Infos.slice(1);
- 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;
+ for (unsigned ElemIdx = 0; ElemIdx < NumElements; ++ElemIdx) {
+ if (Infos.empty())
+ break;
+
+ if (Infos[0].Kind == IITDescriptor::Argument) {
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);
+ Type *ElemTy = STy->getElementType(ElemIdx);
+
+ if (!Infos.empty() &&
+ Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+ if (!checkAndConsumeConstraintBlock(
+ ElemTy, ConstraintKind::Allowed, Infos,
+ Twine("Return type struct element ") + Twine(ElemIdx), ErrMsg))
+ return false;
+ } else if (!Infos.empty() &&
+ Infos[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
+ if (!checkAndConsumeConstraintBlock(
+ ElemTy, ConstraintKind::Excluded, Infos,
+ Twine("Return type struct element ") + Twine(ElemIdx), ErrMsg))
+ return false;
}
- }
- } else if (!Infos.empty())
+ } else
+ skipDescriptorsForSingleType(Infos);
+ }
+
+ return true;
+}
+
+// Verify the non struct return types.
+static bool
+verifyIntrinsicNonStructOutputTypes(Type *RetTy,
+ ArrayRef<Intrinsic::IITDescriptor> &Infos,
+ std::string &ErrMsg) {
+ using namespace Intrinsic;
+
+ if (Infos.empty())
+ return true;
+
+ if (Infos[0].Kind != IITDescriptor::Argument) {
skipDescriptorsForSingleType(Infos);
+ return true;
+ }
+
+ Infos = Infos.slice(1);
+
+ if (!Infos.empty() &&
+ Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
+ if (!checkAndConsumeConstraintBlock(RetTy, ConstraintKind::Allowed, Infos,
+ "Return type", ErrMsg))
+ return false;
+ } else if (!Infos.empty() &&
+ Infos[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
+ if (!checkAndConsumeConstraintBlock(RetTy, ConstraintKind::Excluded, Infos,
+ "Return type", ErrMsg))
+ return false;
+ }
+
+ return true;
+}
+
+// Verify the return types.
+static bool
+verifyIntrinsicOutputTypes(Intrinsic::ID id, FunctionType *FTy,
+ ArrayRef<Intrinsic::IITDescriptor> &Infos,
+ std::string &ErrMsg) {
+
+ Type *RetTy = FTy->getReturnType();
+ if (RetTy->isStructTy())
+ return verifyIntrinsicStructOutputTypes(RetTy, Infos, ErrMsg);
+
+ return verifyIntrinsicNonStructOutputTypes(RetTy, Infos, ErrMsg);
+}
+
+// Verify the input parameters.
+static bool verifyIntrinsicInputTypes(Intrinsic::ID id, FunctionType *FTy,
+ ArrayRef<Intrinsic::IITDescriptor> &Infos,
+ std::string &ErrMsg) {
+ using namespace Intrinsic;
+
+ SmallVector<Type *, 4> ArgTys;
- // Processing parameters.
for (unsigned ParamIdx = 0; ParamIdx < FTy->getNumParams(); ++ParamIdx) {
if (Infos.empty())
break;
@@ -1352,21 +1428,61 @@ bool Intrinsic::verifyIntrinsicTypeConstraints(ID id, FunctionType *FTy,
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";
+ if (!checkAndConsumeConstraintBlock(
+ ParamTy, ConstraintKind::Allowed, Infos,
+ Twine("Parameter ") + Twine(ParamIdx), ErrMsg))
+ return false;
+ if (ArgNum == ArgTys.size())
+ ArgTys.push_back(ParamTy);
+ } else if (!Infos.empty() &&
+ Infos[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
+ if (!checkAndConsumeConstraintBlock(
+ ParamTy, ConstraintKind::Excluded, Infos,
+ Twine("Parameter ") + Twine(ParamIdx), ErrMsg))
return false;
- }
-
if (ArgNum == ArgTys.size())
ArgTys.push_back(ParamTy);
}
} else
skipDescriptorsForSingleType(Infos);
}
+
+ return true;
+}
+
+bool Intrinsic::verifyIntrinsicTypeConstraints(ID id, FunctionType *FTy,
+ std::string &ErrMsg) {
+ if (id == 0 || id >= Intrinsic::num_intrinsics)
+ return true;
+
+ if (!isOverloaded(id))
+ return true;
+
+ SmallVector<IITDescriptor, 8> Table;
+ getIntrinsicInfoTableEntries(id, Table);
+ if (Table.empty())
+ return true;
+
+ ArrayRef<IITDescriptor> Infos = Table;
+
+ // if there are no constraint descriptors, skip verification.
+ bool HasConstraints = false;
+ for (const auto &D : Infos) {
+ if (D.Kind == IITDescriptor::ArgumentTypeConstraint ||
+ D.Kind == IITDescriptor::ArgumentTypeExclusion) {
+ HasConstraints = true;
+ break;
+ }
+ }
+ if (!HasConstraints)
+ return true;
+
+ if (!verifyIntrinsicOutputTypes(id, FTy, Infos, ErrMsg))
+ return false;
+
+ if (!verifyIntrinsicInputTypes(id, FTy, Infos, ErrMsg))
+ return false;
+
return true;
}
diff --git a/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll
index 7647a04898299..faf97a1875dab 100644
--- a/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll
+++ b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll
@@ -1,39 +1,39 @@
; RUN: not llvm-as < %s -o /dev/null
-; Test cases for int_nvvm_test_anytype intrinsic - INVALID combinations
+; Test cases for int_nvvm_test_type 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)
+define i32 @invalid_arg0_i8(i8 %a0, i8 %a1, i32 %a2, ptr %a3, i32 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i8.i8.i32.p0.i32.i16(i8 %a0, i8 %a1, i32 %a2, ptr %a3, i32 %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)
+define i32 @invalid_arg1_mismatch_i16_i32(i16 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i16.i32.i32.p0.i32.i16(i16 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %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)
+define i32 @invalid_arg2_double(i32 %a0, i32 %a1, double %a2, ptr %a3, i32 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i32.i32.f64.p0.i32.i16(i32 %a0, i32 %a1, double %a2, ptr %a3, i32 %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)
+define i32 @invalid_arg3_ptr_as1_wrong(i32 %a0, i32 %a1, i32 %a2, ptr addrspace(1) %a3, i32 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i32.i32.i32.p1.i32.i16(i32 %a0, i32 %a1, i32 %a2, ptr addrspace(1) %a3, i32 %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)
+; arg4 must not be i64 or double
+define i32 @test_arg4_double(i32 %a0, i32 %a1, i32 %a2, ptr %a3, double %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i32.i32.i32.p0.f64.i16(i32 %a0, i32 %a1, i32 %a2, ptr %a3, double %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)
+define i32 @invalid_arg5_v2i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, <2 x i32> %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i32.v2i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, <2 x i32> %a5)
ret i32 %result
}
@@ -42,10 +42,10 @@ define {i32, i8, i64} @invalid_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 @llvm.nvvm.test.type.i8.i8.i32.p0.i32.i16(i8, i8, i32, ptr, i32, i16)
+declare i32 @llvm.nvvm.test.type.i16.i32.i32.p0.i32.i16(i16, i32, i32, ptr, i32, i16)
+declare i32 @llvm.nvvm.test.type.i32.i32.f64.p0.i32.i16(i32, i32, double, ptr, i32, i16)
+declare i32 @llvm.nvvm.test.type.i32.i32.i32.p1.i32.i16(i32, i32, i32, ptr addrspace(1), i32, i16)
+declare i32 @llvm.nvvm.test.type.i32.i32.i32.p0.f64.i16(i32, i32, i32, ptr, double, i16)
+declare i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i32.v2i32(i32, i32, i32, ptr, i32, <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
index 2efa46c04116f..95fb9c9c30157 100644
--- a/llvm/test/CodeGen/NVPTX/anytypeof_constraints_valid.ll
+++ b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_valid.ll
@@ -1,33 +1,33 @@
; RUN: llvm-as < %s -o /dev/null
-; Test cases for int_nvvm_test_anytype intrinsic - VALID combinations
+; Test cases for int_nvvm_test_type 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)
+define i32 @test_arg0_i16(i16 %a0, i16 %a1, i32 %a2, ptr %a3, i32 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i16.i16.i32.p0.i32.i16(i16 %a0, i16 %a1, i32 %a2, ptr %a3, i32 %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)
+define i32 @test_arg0_i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i32.i16(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %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)
+define i32 @test_arg2_anyint(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i32.i16(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %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)
+define i32 @test_arg3_shared_ptr(i32 %a0, i32 %a1, i32 %a2, ptr addrspace(3) %a3, i32 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i32.i32.i32.p3.i32.i16(i32 %a0, i32 %a1, i32 %a2, ptr addrspace(3) %a3, i32 %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)
+define i32 @invalid_arg4_i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i16 %a4, i16 %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i16.i16(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i16 %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)
+define i32 @test_arg5_v4i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, <4 x i32> %a5) {
+ %result = call i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i32.v4i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, <4 x i32> %a5)
ret i32 %result
}
@@ -36,10 +36,10 @@ define {i32, i32, i64} @test_return_type_valid(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 @llvm.nvvm.test.type.i16.i16.i32.p0.i32.i16(i16, i16, i32, ptr, i32, i16)
+declare i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i32.i16(i32, i32, i32, ptr, i32, i16)
+declare i32 @llvm.nvvm.test.type.i32.i32.f32.p0.i32.i16(i32, i32, float, ptr, i32, i16)
+declare i32 @llvm.nvvm.test.type.i32.i32.i32.p3.i32.i16(i32, i32, i32, ptr addrspace(3), i32, i16)
+declare i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i16.i16(i32, i32, i32, ptr, i16, i16)
+declare i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i32.v4i32(i32, i32, i32, ptr, i32, <4 x i32>)
declare {i32, i32, i64} @llvm.nvvm.test.return.type.i32(i32)
diff --git a/llvm/test/TableGen/overloaded-intrinsic-constraints.td b/llvm/test/TableGen/overloaded-intrinsic-constraints.td
new file mode 100644
index 0000000000000..b6f04ded18bb5
--- /dev/null
+++ b/llvm/test/TableGen/overloaded-intrinsic-constraints.td
@@ -0,0 +1,43 @@
+// RUN: llvm-tblgen -gen-intrinsic-enums -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS | FileCheck %s --check-prefix=CHECK-ENUM
+// RUN: llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS | FileCheck %s --check-prefix=CHECK-IMPL
+
+include "llvm/IR/Intrinsics.td"
+
+// Test AnyTypeOf constraints for overloaded intrinsics.
+def int_anytypeof_multi : Intrinsic<
+ [llvm_i32_ty],
+ [
+ AnyTypeOf<[llvm_i16_ty, llvm_i32_ty]>,
+ AnyTypeOf<[llvm_anyint_ty, llvm_float_ty, llvm_double_ty]>
+ ],
+ [IntrNoMem]
+>;
+
+// Test mixed AnyTypeOf + NoneTypeOf.
+def int_mixed_constraints : Intrinsic<
+ [llvm_i32_ty],
+ [
+ AnyTypeOf<[llvm_i16_ty, llvm_i32_ty]>,
+ NoneTypeOf<[llvm_i8_ty]>
+ ],
+ [IntrNoMem]
+>;
+
+// Test NoneTypeOf on return type.
+def int_nonetypeof_return : Intrinsic<
+ [NoneTypeOf<[llvm_i8_ty, llvm_double_ty]>],
+ [llvm_i32_ty],
+ [IntrNoMem]
+>;
+
+// Verify enum generation.
+// CHECK-ENUM: anytypeof_multi = {{[0-9]+}}, // llvm.anytypeof.multi
+// CHECK-ENUM: mixed_constraints{{.*}}// llvm.mixed.constraints
+// CHECK-ENUM: nonetypeof_return{{.*}}// llvm.nonetypeof.return
+
+// Verify encoding table generation.
+// CHECK-IMPL: static constexpr unsigned char IIT_LongEncodingTable[] = {
+// CHECK-IMPL-NEXT: /* 0 */ 4, 15, 0, 93, 2, 3, 4, 15, 8, 94, 1, 2, 0,
+// CHECK-IMPL-NEXT: /* 13 */ 15, 0, 94, 2, 2, 8, 4, 0,
+// CHECK-IMPL-NEXT: /* 21 */ 4, 15, 0, 93, 2, 3, 4, 15, 8, 93, 3, 15, 9, 7, 8, 0,
+// CHECK-IMPL-NEXT: 255
>From 13c5b1f7ea2ff273dd2d83c0eb68c5126ff45354 Mon Sep 17 00:00:00 2001
From: Dharuni R Acharya <dharuniracharya at gmail.com>
Date: Sun, 11 Jan 2026 16:29:33 +0000
Subject: [PATCH 4/4] [LLVM-Tablegen] Except predicate for overloaded
intrinsics with constraints
This patch introduces an Except constraint for overloaded intrinsics to exclude specific type
combinations produced by AnyTypeOf/NoneTypeOf products.
It builds on the explicit type-constraint infrastructure introduced in PR 172442.
Link to the RFC, where this feature was discussed:
https://discourse.llvm.org/t/rfc-tablegen-explicit-type-constraints-for-overloaded-llvm-intrinsics/89142
Signed-off-by: Dharuni R Acharya <dharuniracharya at gmail.com>
---
llvm/include/llvm/IR/Intrinsics.h | 17 +
llvm/include/llvm/IR/Intrinsics.td | 37 +++
llvm/include/llvm/IR/IntrinsicsNVVM.td | 18 +-
llvm/lib/IR/Intrinsics.cpp | 295 +++++++++++++-----
.../NVPTX/anytypeof_constraints_invalid.ll | 20 +-
.../NVPTX/anytypeof_constraints_valid.ll | 12 +-
.../utils/TableGen/Basic/IntrinsicEmitter.cpp | 28 ++
7 files changed, 342 insertions(+), 85 deletions(-)
diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h
index dd782a6b5b895..89e2239028329 100644
--- a/llvm/include/llvm/IR/Intrinsics.h
+++ b/llvm/include/llvm/IR/Intrinsics.h
@@ -184,6 +184,7 @@ namespace Intrinsic {
ArgumentTypeConstraint, // For AnyTypeOf - marks constrained argument
// types.
ArgumentTypeExclusion, // For NoneTypeOf - marks excluded argument types.
+ ExceptConstraint, // For Except - marks excluded combinations.
} Kind;
union {
@@ -247,6 +248,14 @@ namespace Intrinsic {
return Argument_NumConstraints;
}
+ // For ExceptConstraint: get number of combinations and size.
+ std::pair<unsigned, unsigned> getExceptInfo() const {
+ assert(Kind == ExceptConstraint);
+ unsigned NumCombos = Argument_Info >> 16;
+ unsigned ComboSize = Argument_Info & 0xFFFF;
+ return {NumCombos, ComboSize};
+ }
+
static IITDescriptor get(IITDescriptorKind K, unsigned Field) {
IITDescriptor Result = { K, { Field } };
return Result;
@@ -264,6 +273,14 @@ namespace Intrinsic {
Result.Vector_Width = ElementCount::get(Width, IsScalable);
return Result;
}
+
+ static IITDescriptor getExcept(unsigned NumCombos, unsigned ComboSize) {
+ assert(NumCombos <= 0xFFFF && "NumCombos exceeds 16 bits");
+ assert(ComboSize <= 0xFFFF && "ComboSize exceeds 16 bits");
+ return get(ExceptConstraint,
+ static_cast<unsigned short>(NumCombos),
+ static_cast<unsigned short>(ComboSize));
+ }
};
/// Return the IIT table descriptor for the specified intrinsic into an array
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 0f02a77b5323e..199dd48cdfb6f 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -375,6 +375,7 @@ def IIT_V4096 : IIT_Vec<4096, 53>;
// Constrained type encoding for overloaded intrinsics.
def IIT_ANYTYPE : IIT_Base<93>;
def IIT_NONETYPE : IIT_Base<94>;
+def IIT_EXCEPT : IIT_Base<95>;
}
defvar IIT_all_FixedTypes = !filter(iit, IIT_all,
@@ -485,6 +486,29 @@ class NoneTypeOf<list<LLVMType> excluded_types> : LLVMAnyType<Any> {
);
}
+// Except: To constraint specific combinations allowed by cartesian product of AnyTypeOfs/NoneTypeOfs.
+// Encoding: [IIT_EXCEPT, num_combinations, combo_size, type_sigs...]
+class Except<list<list<LLVMType>> invalid_combinations> {
+ list<list<LLVMType>> InvalidCombinations = invalid_combinations;
+
+ // Validate: at least one combination.
+ assert !gt(!size(invalid_combinations), 0),
+ "Except must have at least one invalid combination";
+
+ // Validate: all combinations have same size.
+ defvar first_size = !size(!head(invalid_combinations));
+ assert !foldl(1, invalid_combinations, valid, combo,
+ !and(valid, !eq(!size(combo), first_size))),
+ "All Except combinations must have the same number of types";
+
+ list<int> Sig = !listconcat(
+ [IIT_EXCEPT.Number, !size(invalid_combinations), first_size],
+ !foldl([]<int>, invalid_combinations, accum, combo,
+ !listconcat(accum, !foldl([]<int>, combo, accum2, ty,
+ !listconcat(accum2, 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:
@@ -780,6 +804,19 @@ class DefaultAttrsIntrinsic<list<LLVMType> ret_types,
intr_properties, name,
sd_properties, /*disable_default_attributes*/ 0> {}
+class IntrinsicWithExcept<list<LLVMType> ret_types,
+ list<LLVMType> param_types = [],
+ list<IntrinsicProperty> intr_properties = [],
+ list<Except> except_constraints = [],
+ string name = "",
+ list<SDNodeProperty> sd_properties = [],
+ bit disable_default_attributes = true>
+ : Intrinsic<ret_types, param_types, intr_properties, name, sd_properties,
+ disable_default_attributes> {
+
+ list<Except> ExceptConstraints = except_constraints;
+}
+
/// ClangBuiltin - If this intrinsic exactly corresponds to a Clang builtin, this
/// specifies the name of the builtin. This provides automatic CBE and CFE
/// support.
diff --git a/llvm/include/llvm/IR/IntrinsicsNVVM.td b/llvm/include/llvm/IR/IntrinsicsNVVM.td
index d0bb6b5232071..4b948dea55e9d 100644
--- a/llvm/include/llvm/IR/IntrinsicsNVVM.td
+++ b/llvm/include/llvm/IR/IntrinsicsNVVM.td
@@ -3392,9 +3392,19 @@ def int_nvvm_test_type : 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]>, NoneTypeOf<[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]>;
+// IIT_LongEncodingTable entry: /* 15435 */ 21, 1, 4, 15, 0, 93, 3, 2, 3, 4, 5, 4, 15, 8, 93, 2, 7, 8, 95, 1, 2, 2, 7, 0,
+def int_nvvm_test_return_type : IntrinsicWithExcept<[llvm_i32_ty, AnyTypeOf<[llvm_i8_ty, llvm_i16_ty, llvm_i32_ty]>, llvm_i64_ty],
+ [llvm_i32_ty, AnyTypeOf<[llvm_float_ty, llvm_double_ty]>],
+ [IntrNoMem],
+ [Except<[[llvm_i8_ty, llvm_float_ty]]>]>;
+
+// IIT_LongEncodingTable entry: /* 16724 */ 3, 7, 15, 0, 93, 3, 3, 10, 4, 10, 7, 15, 8, 93, 2, 4, 7, 95, 2, 2, 10, 4, 7, 10, 7, 7, 0,
+def int_nvvm_except : IntrinsicWithExcept<[llvm_i16_ty],
+ [llvm_float_ty,
+ AnyTypeOf<[llvm_i16_ty, llvm_v4i32_ty, llvm_v4f32_ty]>,
+ AnyTypeOf<[llvm_i32_ty, llvm_float_ty]>],
+ [IntrNoMem],
+ [Except<[[llvm_v4i32_ty, llvm_float_ty],
+ [llvm_v4f32_ty, llvm_float_ty]]>]>;
} // let TargetPrefix = "nvvm"
diff --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index 91720067e3a41..a79fbf43a7fcd 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -383,6 +383,17 @@ DecodeIITType(unsigned &NextElt, ArrayRef<unsigned char> Infos,
llvm_unreachable("IIT_ANYTYPE must follow IIT_ARG");
case IIT_NONETYPE:
llvm_unreachable("IIT_NONETYPE must follow IIT_ARG");
+ case IIT_EXCEPT: {
+ unsigned NumCombos = Infos[NextElt++];
+ unsigned ComboSize = Infos[NextElt++];
+
+ OutputTable.push_back(IITDescriptor::getExcept(NumCombos, ComboSize));
+
+ for (unsigned i = 0; i < NumCombos * ComboSize; ++i)
+ DecodeIITType(NextElt, Infos, Info, OutputTable);
+
+ return;
+ }
case IIT_EXTEND_ARG: {
unsigned ArgInfo = (NextElt == Infos.size() ? 0 : Infos[NextElt++]);
OutputTable.push_back(
@@ -543,8 +554,22 @@ static Type *DecodeFixedType(ArrayRef<Intrinsic::IITDescriptor> &Infos,
return PointerType::get(Context, D.Pointer_AddressSpace);
case IITDescriptor::Struct: {
SmallVector<Type *, 8> Elts;
- for (unsigned i = 0, e = D.Struct_NumElements; i != e; ++i)
+ for (unsigned i = 0, e = D.Struct_NumElements; i != e; ++i) {
Elts.push_back(DecodeFixedType(Infos, Tys, Context));
+ if (!Infos.empty() &&
+ Infos.front().Kind == IITDescriptor::ArgumentTypeConstraint) {
+ unsigned NumConstraints = Infos.front().getArgumentNumConstraints();
+ Infos = Infos.slice(1);
+ for (unsigned j = 0; j < NumConstraints; ++j)
+ (void)DecodeFixedType(Infos, Tys, Context);
+ } else if (!Infos.empty() &&
+ Infos.front().Kind == IITDescriptor::ArgumentTypeExclusion) {
+ unsigned NumExclusions = Infos.front().getArgumentNumExclusions();
+ Infos = Infos.slice(1);
+ for (unsigned j = 0; j < NumExclusions; ++j)
+ (void)DecodeFixedType(Infos, Tys, Context);
+ }
+ }
return StructType::get(Context, Elts);
}
case IITDescriptor::Argument:
@@ -604,10 +629,95 @@ static Type *DecodeFixedType(ArrayRef<Intrinsic::IITDescriptor> &Infos,
case IITDescriptor::ArgumentTypeExclusion:
llvm_unreachable(
"ArgumentTypeExclusion should not appear in DecodeFixedType");
+ case IITDescriptor::ExceptConstraint:
+ llvm_unreachable("ExceptConstraint should not appear in DecodeFixedType");
}
llvm_unreachable("unhandled");
}
+// 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);
+ if (!Infos.empty() &&
+ Infos.front().Kind == IITDescriptor::ArgumentTypeConstraint) {
+ unsigned NumConstraints = Infos.front().getArgumentNumConstraints();
+ Count++;
+ Infos = Infos.slice(1);
+ for (unsigned j = 0; j < NumConstraints; ++j)
+ Count += skipDescriptorsForSingleType(Infos);
+ } else if (!Infos.empty() &&
+ Infos.front().Kind == IITDescriptor::ArgumentTypeExclusion) {
+ unsigned NumExclusions = Infos.front().getArgumentNumExclusions();
+ Count++;
+ Infos = Infos.slice(1);
+ for (unsigned j = 0; j < NumExclusions; ++j)
+ 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);
+ }
+ if (!Infos.empty() &&
+ Infos[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
+ unsigned NumExclusions = Infos[0].getArgumentNumExclusions();
+ Count++;
+ Infos = Infos.slice(1);
+ for (unsigned i = 0; i < NumExclusions; ++i)
+ Count += skipDescriptorsForSingleType(Infos);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return Count;
+}
+
+// Skip Except constraint descriptors.
+static void skipExceptDescriptors(ArrayRef<Intrinsic::IITDescriptor> &Infos) {
+ if (Infos.empty() || Infos[0].Kind != Intrinsic::IITDescriptor::ExceptConstraint)
+ return;
+
+ auto [NumCombos, ComboSize] = Infos[0].getExceptInfo();
+ Infos = Infos.slice(1);
+
+ for (unsigned i = 0; i < NumCombos * ComboSize; ++i)
+ skipDescriptorsForSingleType(Infos);
+}
+
FunctionType *Intrinsic::getType(LLVMContext &Context, ID id,
ArrayRef<Type *> Tys) {
SmallVector<IITDescriptor, 8> Table;
@@ -636,6 +746,9 @@ FunctionType *Intrinsic::getType(LLVMContext &Context, ID id,
SmallVector<Type *, 8> ArgTys;
while (!TableRef.empty()) {
+ if (TableRef[0].Kind == IITDescriptor::ExceptConstraint ||
+ TableRef[0].Kind == IITDescriptor::VarArg)
+ break;
ArgTys.push_back(DecodeFixedType(TableRef, Tys, Context));
if (!TableRef.empty() &&
@@ -657,6 +770,8 @@ FunctionType *Intrinsic::getType(LLVMContext &Context, ID id,
}
}
+ skipExceptDescriptors(TableRef);
+
// DecodeFixedType returns Void for IITDescriptor::Void and
// IITDescriptor::VarArg If we see void type as the type of the last argument,
// it is vararg intrinsic
@@ -664,6 +779,11 @@ FunctionType *Intrinsic::getType(LLVMContext &Context, ID id,
ArgTys.pop_back();
return FunctionType::get(ResultTy, ArgTys, true);
}
+
+ // Check if VarArg descriptor is present.
+ if (!TableRef.empty() && TableRef[0].Kind == IITDescriptor::VarArg)
+ return FunctionType::get(ResultTy, ArgTys, true);
+
return FunctionType::get(ResultTy, ArgTys, false);
}
@@ -904,61 +1024,6 @@ 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);
- }
- if (!Infos.empty() &&
- Infos[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
- unsigned NumExclusions = Infos[0].getArgumentNumExclusions();
- Count++;
- Infos = Infos.slice(1);
- for (unsigned i = 0; i < NumExclusions; ++i)
- Count += skipDescriptorsForSingleType(Infos);
- }
- break;
-
- default:
- break;
- }
- return Count;
-}
-
using DeferredIntrinsicMatchPair =
std::pair<Type *, ArrayRef<Intrinsic::IITDescriptor>>;
@@ -1221,6 +1286,8 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
case IITDescriptor::ArgumentTypeExclusion:
llvm_unreachable(
"ArgumentTypeExclusion should be handled in Argument case");
+ case IITDescriptor::ExceptConstraint:
+ llvm_unreachable("ExceptConstraint should be handled in verification");
}
llvm_unreachable("unhandled");
}
@@ -1255,7 +1322,8 @@ Intrinsic::matchIntrinsicSignature(FunctionType *FTy,
// matchIntrinsicType.
static bool
verifyTypeAgainstConstraints(Type *Ty, unsigned NumConstraints,
- ArrayRef<Intrinsic::IITDescriptor> &Infos) {
+ ArrayRef<Intrinsic::IITDescriptor> &Infos,
+ SmallVector<Type *, 4> &ResolvedTypes) {
using namespace Intrinsic;
bool Matched = false;
@@ -1273,7 +1341,8 @@ verifyTypeAgainstConstraints(Type *Ty, unsigned NumConstraints,
}
skipDescriptorsForSingleType(Infos);
}
-
+ if (Matched)
+ ResolvedTypes.push_back(Ty);
return Matched;
}
@@ -1293,7 +1362,8 @@ enum class ConstraintKind {
static bool
checkAndConsumeConstraintBlock(Type *Ty, ConstraintKind Kind,
ArrayRef<Intrinsic::IITDescriptor> &Infos,
- const Twine &Constraint, std::string &ErrMsg) {
+ const Twine &Constraint, std::string &ErrMsg,
+ SmallVector<Type *, 4> &ResolvedTypes) {
using namespace Intrinsic;
unsigned Count = Kind == ConstraintKind::Allowed
@@ -1301,7 +1371,9 @@ checkAndConsumeConstraintBlock(Type *Ty, ConstraintKind Kind,
: Infos.front().getArgumentNumExclusions();
Infos = Infos.slice(1);
- bool Matches = verifyTypeAgainstConstraints(Ty, Count, Infos);
+ bool Matches = verifyTypeAgainstConstraints(Ty, Count, Infos, ResolvedTypes);
+ if (Kind == ConstraintKind::Excluded && !Matches)
+ ResolvedTypes.push_back(Ty);
bool Violates = Kind == ConstraintKind::Allowed ? !Matches : Matches;
if (!Violates)
return true;
@@ -1317,7 +1389,7 @@ checkAndConsumeConstraintBlock(Type *Ty, ConstraintKind Kind,
static bool
verifyIntrinsicStructOutputTypes(Type *RetTy,
ArrayRef<Intrinsic::IITDescriptor> &Infos,
- std::string &ErrMsg) {
+ std::string &ErrMsg, SmallVector<Type *, 4> &ResolvedTypes) {
using namespace Intrinsic;
auto *STy = dyn_cast<StructType>(RetTy);
@@ -1347,13 +1419,13 @@ verifyIntrinsicStructOutputTypes(Type *RetTy,
Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
if (!checkAndConsumeConstraintBlock(
ElemTy, ConstraintKind::Allowed, Infos,
- Twine("Return type struct element ") + Twine(ElemIdx), ErrMsg))
+ Twine("Return type struct element ") + Twine(ElemIdx), ErrMsg, ResolvedTypes))
return false;
} else if (!Infos.empty() &&
Infos[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
if (!checkAndConsumeConstraintBlock(
ElemTy, ConstraintKind::Excluded, Infos,
- Twine("Return type struct element ") + Twine(ElemIdx), ErrMsg))
+ Twine("Return type struct element ") + Twine(ElemIdx), ErrMsg, ResolvedTypes))
return false;
}
} else
@@ -1367,7 +1439,7 @@ verifyIntrinsicStructOutputTypes(Type *RetTy,
static bool
verifyIntrinsicNonStructOutputTypes(Type *RetTy,
ArrayRef<Intrinsic::IITDescriptor> &Infos,
- std::string &ErrMsg) {
+ std::string &ErrMsg, SmallVector<Type *, 4> &ResolvedTypes) {
using namespace Intrinsic;
if (Infos.empty())
@@ -1383,12 +1455,12 @@ verifyIntrinsicNonStructOutputTypes(Type *RetTy,
if (!Infos.empty() &&
Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
if (!checkAndConsumeConstraintBlock(RetTy, ConstraintKind::Allowed, Infos,
- "Return type", ErrMsg))
+ "Return type", ErrMsg, ResolvedTypes))
return false;
} else if (!Infos.empty() &&
Infos[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
if (!checkAndConsumeConstraintBlock(RetTy, ConstraintKind::Excluded, Infos,
- "Return type", ErrMsg))
+ "Return type", ErrMsg, ResolvedTypes))
return false;
}
@@ -1399,19 +1471,19 @@ verifyIntrinsicNonStructOutputTypes(Type *RetTy,
static bool
verifyIntrinsicOutputTypes(Intrinsic::ID id, FunctionType *FTy,
ArrayRef<Intrinsic::IITDescriptor> &Infos,
- std::string &ErrMsg) {
+ std::string &ErrMsg, SmallVector<Type *, 4> &ResolvedTypes) {
Type *RetTy = FTy->getReturnType();
if (RetTy->isStructTy())
- return verifyIntrinsicStructOutputTypes(RetTy, Infos, ErrMsg);
+ return verifyIntrinsicStructOutputTypes(RetTy, Infos, ErrMsg, ResolvedTypes);
- return verifyIntrinsicNonStructOutputTypes(RetTy, Infos, ErrMsg);
+ return verifyIntrinsicNonStructOutputTypes(RetTy, Infos, ErrMsg, ResolvedTypes);
}
// Verify the input parameters.
static bool verifyIntrinsicInputTypes(Intrinsic::ID id, FunctionType *FTy,
ArrayRef<Intrinsic::IITDescriptor> &Infos,
- std::string &ErrMsg) {
+ std::string &ErrMsg, SmallVector<Type *, 4> &ResolvedTypes) {
using namespace Intrinsic;
SmallVector<Type *, 4> ArgTys;
@@ -1430,7 +1502,7 @@ static bool verifyIntrinsicInputTypes(Intrinsic::ID id, FunctionType *FTy,
Infos[0].Kind == IITDescriptor::ArgumentTypeConstraint) {
if (!checkAndConsumeConstraintBlock(
ParamTy, ConstraintKind::Allowed, Infos,
- Twine("Parameter ") + Twine(ParamIdx), ErrMsg))
+ Twine("Parameter ") + Twine(ParamIdx), ErrMsg, ResolvedTypes))
return false;
if (ArgNum == ArgTys.size())
ArgTys.push_back(ParamTy);
@@ -1438,7 +1510,7 @@ static bool verifyIntrinsicInputTypes(Intrinsic::ID id, FunctionType *FTy,
Infos[0].Kind == IITDescriptor::ArgumentTypeExclusion) {
if (!checkAndConsumeConstraintBlock(
ParamTy, ConstraintKind::Excluded, Infos,
- Twine("Parameter ") + Twine(ParamIdx), ErrMsg))
+ Twine("Parameter ") + Twine(ParamIdx), ErrMsg, ResolvedTypes))
return false;
if (ArgNum == ArgTys.size())
ArgTys.push_back(ParamTy);
@@ -1450,6 +1522,69 @@ static bool verifyIntrinsicInputTypes(Intrinsic::ID id, FunctionType *FTy,
return true;
}
+// Verify Except constraints.
+static bool
+verifyExceptConstraints(ArrayRef<Intrinsic::IITDescriptor> &Infos,
+ const SmallVector<Type *, 4> &ResolvedTypes,
+ LLVMContext &Context,
+ std::string &ErrMsg) {
+ using namespace Intrinsic;
+
+ if (Infos.empty() || Infos[0].Kind != IITDescriptor::ExceptConstraint)
+ return true;
+
+ auto [NumCombos, ComboSize] = Infos[0].getExceptInfo();
+ Infos = Infos.slice(1);
+
+ if (ResolvedTypes.size() != ComboSize) {
+ ErrMsg = "Internal error: Except constraint expects " +
+ std::to_string(ComboSize) + " resolved types but got " +
+ std::to_string(ResolvedTypes.size());
+ return false;
+ }
+
+ for (unsigned ComboIdx = 0; ComboIdx < NumCombos; ++ComboIdx) {
+ SmallVector<Type *, 4> ExcludedCombo;
+ ExcludedCombo.reserve(ComboSize);
+
+ for (unsigned TypeIdx = 0; TypeIdx < ComboSize; ++TypeIdx) {
+ SmallVector<Type *, 4> Unused;
+ Type *Ty = DecodeFixedType(Infos, Unused, Context);
+ if (!Ty) {
+ ErrMsg = "Failed to decode Except excluded type at combination " +
+ std::to_string(ComboIdx) + ", position " +
+ std::to_string(TypeIdx);
+ return false;
+ }
+ ExcludedCombo.push_back(Ty);
+ }
+
+ bool MatchExcept = true;
+ for (unsigned TypeIdx = 0; TypeIdx < ComboSize; ++TypeIdx) {
+ if (ExcludedCombo[TypeIdx] != ResolvedTypes[TypeIdx]) {
+ MatchExcept = false;
+ break;
+ }
+ }
+
+ if (MatchExcept) {
+ std::string ComboStr = "(";
+ for (unsigned TypeIdx = 0; TypeIdx < ComboSize; ++TypeIdx) {
+ if (TypeIdx > 0)
+ ComboStr += ", ";
+ ComboStr += typeToString(ResolvedTypes[TypeIdx]);
+ }
+ ComboStr += ")";
+
+ ErrMsg = "Type combination " + ComboStr +
+ " is excluded by Except constraint";
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool Intrinsic::verifyIntrinsicTypeConstraints(ID id, FunctionType *FTy,
std::string &ErrMsg) {
if (id == 0 || id >= Intrinsic::num_intrinsics)
@@ -1469,7 +1604,8 @@ bool Intrinsic::verifyIntrinsicTypeConstraints(ID id, FunctionType *FTy,
bool HasConstraints = false;
for (const auto &D : Infos) {
if (D.Kind == IITDescriptor::ArgumentTypeConstraint ||
- D.Kind == IITDescriptor::ArgumentTypeExclusion) {
+ D.Kind == IITDescriptor::ArgumentTypeExclusion ||
+ D.Kind == IITDescriptor::ExceptConstraint) {
HasConstraints = true;
break;
}
@@ -1477,10 +1613,15 @@ bool Intrinsic::verifyIntrinsicTypeConstraints(ID id, FunctionType *FTy,
if (!HasConstraints)
return true;
- if (!verifyIntrinsicOutputTypes(id, FTy, Infos, ErrMsg))
+ SmallVector<Type *, 4> ResolvedTypes;
+
+ if (!verifyIntrinsicOutputTypes(id, FTy, Infos, ErrMsg, ResolvedTypes))
+ return false;
+
+ if (!verifyIntrinsicInputTypes(id, FTy, Infos, ErrMsg, ResolvedTypes))
return false;
- if (!verifyIntrinsicInputTypes(id, FTy, Infos, ErrMsg))
+ if (!verifyExceptConstraints(Infos, ResolvedTypes, FTy->getContext(), ErrMsg))
return false;
return true;
@@ -1488,6 +1629,10 @@ bool Intrinsic::verifyIntrinsicTypeConstraints(ID id, FunctionType *FTy,
bool Intrinsic::matchIntrinsicVarArg(
bool isVarArg, ArrayRef<Intrinsic::IITDescriptor> &Infos) {
+
+ // Skip Except descriptors - they are constraint descriptors, not part of the signature.
+ skipExceptDescriptors(Infos);
+
// If there are no descriptors left, then it can't be a vararg.
if (Infos.empty())
return isVarArg;
diff --git a/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll
index faf97a1875dab..d597a469ce7cd 100644
--- a/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll
+++ b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_invalid.ll
@@ -37,10 +37,22 @@ define i32 @invalid_arg5_v2i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, <2 x
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)
+; return type struct 1st element must be i16 or i32 or i8 and except combination
+define {i32, i8, i64} @invalid_return_type_i8(i32 %arg, float %a1) {
+ %result = call {i32, i8, i64} @llvm.nvvm.test.return.type.i8.f32(i32 %arg, float %a1)
ret {i32, i8, i64} %result
}
+
+; except combinations
+define i16 @test_except(float %a1, <4 x i32> %a2, float %a3) {
+ %result = call i16 @llvm.nvvm.except.v4i32.f32(float %a1, <4 x i32> %a2, float %a3)
+ ret i16 %result
+}
+
+define i16 @test_except_2(float %a1, <4 x float> %a2, float %a3) {
+ %result = call i16 @llvm.nvvm.except.v4f32.f32(float %a1, <4 x float> %a2, float %a3)
+ ret i16 %result
+}
declare i32 @llvm.nvvm.test.type.i8.i8.i32.p0.i32.i16(i8, i8, i32, ptr, i32, i16)
declare i32 @llvm.nvvm.test.type.i16.i32.i32.p0.i32.i16(i16, i32, i32, ptr, i32, i16)
@@ -48,4 +60,6 @@ declare i32 @llvm.nvvm.test.type.i32.i32.f64.p0.i32.i16(i32, i32, double, ptr, i
declare i32 @llvm.nvvm.test.type.i32.i32.i32.p1.i32.i16(i32, i32, i32, ptr addrspace(1), i32, i16)
declare i32 @llvm.nvvm.test.type.i32.i32.i32.p0.f64.i16(i32, i32, i32, ptr, double, i16)
declare i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i32.v2i32(i32, i32, i32, ptr, i32, <2 x i32>)
-declare {i32, i8, i64} @llvm.nvvm.test.return.type.i8(i32)
+declare {i32, i8, i64} @llvm.nvvm.test.return.type.i8.f32(i32, float)
+declare i16 @llvm.nvvm.except.v4i32.f32(float, <4 x i32>, float)
+declare i16 @llvm.nvvm.except.v4f32.f32(float, <4 x float>, float)
diff --git a/llvm/test/CodeGen/NVPTX/anytypeof_constraints_valid.ll b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_valid.ll
index 95fb9c9c30157..c736e86a16836 100644
--- a/llvm/test/CodeGen/NVPTX/anytypeof_constraints_valid.ll
+++ b/llvm/test/CodeGen/NVPTX/anytypeof_constraints_valid.ll
@@ -31,10 +31,15 @@ define i32 @test_arg5_v4i32(i32 %a0, i32 %a1, i32 %a2, ptr %a3, i32 %a4, <4 x i3
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)
+define {i32, i32, i64} @test_return_type_valid(i32 %arg, float %a1) {
+ %result = call {i32, i32, i64} @llvm.nvvm.test.return.type.i32(i32 %arg, float %a1)
ret {i32, i32, i64} %result
}
+
+define i16 @test_except(float %a1, <4 x i32> %a2, i32 %a3) {
+ %result = call i16 @llvm.nvvm.except.v4i32.i32.f32(float %a1, <4 x i32> %a2, i32 %a3)
+ ret i16 %result
+}
declare i32 @llvm.nvvm.test.type.i16.i16.i32.p0.i32.i16(i16, i16, i32, ptr, i32, i16)
declare i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i32.i16(i32, i32, i32, ptr, i32, i16)
@@ -42,4 +47,5 @@ declare i32 @llvm.nvvm.test.type.i32.i32.f32.p0.i32.i16(i32, i32, float, ptr, i3
declare i32 @llvm.nvvm.test.type.i32.i32.i32.p3.i32.i16(i32, i32, i32, ptr addrspace(3), i32, i16)
declare i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i16.i16(i32, i32, i32, ptr, i16, i16)
declare i32 @llvm.nvvm.test.type.i32.i32.i32.p0.i32.v4i32(i32, i32, i32, ptr, i32, <4 x i32>)
-declare {i32, i32, i64} @llvm.nvvm.test.return.type.i32(i32)
+declare {i32, i32, i64} @llvm.nvvm.test.return.type.i32.f32(i32, float)
+declare i16 @llvm.nvvm.except.v4i32.i32(float, <4 x i32>, i32)
diff --git a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
index 9fed5920a019f..285c7856a0c69 100644
--- a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
@@ -321,6 +321,32 @@ void IntrinsicEmitter::EmitIntrinsicToOverloadTable(
using TypeSigTy = SmallVector<unsigned char>;
+static void AppendExceptConstraints(const Record *IntDef, TypeSigTy &TypeSig) {
+ if (!IntDef->getValue("ExceptConstraints"))
+ return;
+
+ const ListInit *ExceptList = IntDef->getValueAsListInit("ExceptConstraints");
+ if (!ExceptList || ExceptList->empty())
+ return;
+
+ for (const Init *ExceptInit : ExceptList->getElements()) {
+ const DefInit *DI = dyn_cast<DefInit>(ExceptInit);
+ if (!DI)
+ continue;
+
+ const Record *ExceptRec = DI->getDef();
+ const ListInit *SigList = ExceptRec->getValueAsListInit("Sig");
+ if (!SigList)
+ continue;
+
+ for (const Init *SigByteInit : SigList->getElements()) {
+ const IntInit *II = dyn_cast<IntInit>(SigByteInit);
+ if (II)
+ TypeSig.emplace_back(II->getValue());
+ }
+ }
+}
+
/// Computes type signature of the intrinsic \p Int.
static TypeSigTy ComputeTypeSignature(const CodeGenIntrinsic &Int) {
TypeSigTy TypeSig;
@@ -329,6 +355,8 @@ static TypeSigTy ComputeTypeSignature(const CodeGenIntrinsic &Int) {
for (const auto *TypeListEntry : TypeList->getElements())
TypeSig.emplace_back(cast<IntInit>(TypeListEntry)->getValue());
+
+ AppendExceptConstraints(Int.TheDef, TypeSig);
return TypeSig;
}
More information about the llvm-commits
mailing list