[llvm] 6934ed5 - IR: Add !nofpclass metadata (#177140)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jan 22 11:49:39 PST 2026
Author: Matt Arsenault
Date: 2026-01-22T20:49:34+01:00
New Revision: 6934ed51b3345caf9a4005420d1d4078fa3716b9
URL: https://github.com/llvm/llvm-project/commit/6934ed51b3345caf9a4005420d1d4078fa3716b9
DIFF: https://github.com/llvm/llvm-project/commit/6934ed51b3345caf9a4005420d1d4078fa3716b9.diff
LOG: IR: Add !nofpclass metadata (#177140)
This adds the analogous metadata to the nofpclass attribute
to assert values are not a certain set of floating-point classes.
This allows the same information to be expressed if a function
argument is passed indirectly. This matches the bitmask encoding
of nofpclass.
I also think this should be allowed for stores to symmetrically handle
sret, but leave that for later.
Alternatively we could add a more expressive !fprange metadata,
but that would be much more complex. It's useful to match the attribute,
and more annotations can always be added.
Fixes #133560
Added:
llvm/test/Transforms/GVN/merge-nofpclass.ll
llvm/test/Verifier/nofpclass-metadata.ll
Modified:
llvm/docs/LangRef.rst
llvm/include/llvm/IR/FixedMetadataKinds.def
llvm/include/llvm/IR/Metadata.h
llvm/lib/Analysis/ValueTracking.cpp
llvm/lib/IR/Attributes.cpp
llvm/lib/IR/Metadata.cpp
llvm/lib/IR/Verifier.cpp
llvm/lib/Transforms/Scalar/GVN.cpp
llvm/lib/Transforms/Utils/Local.cpp
llvm/test/Transforms/ArgumentPromotion/metadata.ll
llvm/test/Transforms/Attributor/nofpclass.ll
llvm/test/Transforms/GVN/PRE/load-metadata.ll
llvm/test/Transforms/GVNHoist/hoist-md.ll
llvm/test/Transforms/InstCombine/fold-phi-load-metadata.ll
llvm/test/Transforms/InstCombine/loadstore-metadata.ll
llvm/test/Transforms/SROA/preserve-metadata.ll
llvm/test/Transforms/SimplifyCFG/hoist-with-metadata.ll
llvm/test/tools/llvm-reduce/reduce-operands-to-args-metadata-to-attributes.ll
Removed:
################################################################################
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index faeed3e63f362..a875b9beb71b9 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -7454,6 +7454,38 @@ Examples:
!2 = !{ i8 0, i8 2, i8 3, i8 6 }
!3 = !{ i8 -2, i8 0, i8 3, i8 6 }
+
+.. _nofpclass-metadata:
+
+'``nofpclass``' Metadata
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+``nofpclass`` metadata may be attached only to ``load``
+instructions. It asserts floating-point value classes which will not
+be loaded. The representation is an i32 constant integer encoding a
+10-bit bitmask, with the same meaning and format as the
+:ref:`nofpclass <nofpclass>` attribute. If the loaded value is one of
+the specified floating-point classes, a poison value is returned. The
+interpretation does not depend on the floating-point environment. A
+zero value would be meaningless and is invalid.
+
+Examples:
+
+.. code-block:: llvm
+
+ %not.nan = load float, ptr %p, align 4, !nofpclass !0
+ %not.inf = load <2 x float>, ptr %p, align 4, !nofpclass !1
+ %only.normal = load double, ptr %p, align 8, !nofpclass !2
+ %always.poison = load double, ptr %p, align 8, !nofpclass !3
+ %not.nan.array = load [2 x <2 x float>], ptr %p, !nofpclass !1
+ %not.nan.struct = load { 2 x float }, ptr %p, align 8, !nofpclass !1
+ ...
+ !0 = !{ i32 3 }
+ !1 = !{ i32 516 }
+ !2 = !{ i32 759 }
+ !3 = !{ i32 1023 }
+
+
'``absolute_symbol``' Metadata
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
index 01d22bdd7c996..98129985714b2 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -58,3 +58,4 @@ LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
LLVM_FIXED_MD_KIND(MD_captures, "captures", 44)
LLVM_FIXED_MD_KIND(MD_alloc_token, "alloc_token", 45)
LLVM_FIXED_MD_KIND(MD_implicit_ref, "implicit.ref", 46)
+LLVM_FIXED_MD_KIND(MD_nofpclass, "nofpclass", 47)
diff --git a/llvm/include/llvm/IR/Metadata.h b/llvm/include/llvm/IR/Metadata.h
index 85a7f8fd373c0..441bee3e5bf48 100644
--- a/llvm/include/llvm/IR/Metadata.h
+++ b/llvm/include/llvm/IR/Metadata.h
@@ -144,7 +144,8 @@ class Metadata {
/// Metadata IDs that may generate poison.
constexpr static const unsigned PoisonGeneratingIDs[] = {
- LLVMContext::MD_range, LLVMContext::MD_nonnull, LLVMContext::MD_align};
+ LLVMContext::MD_range, LLVMContext::MD_nonnull, LLVMContext::MD_align,
+ LLVMContext::MD_nofpclass};
};
// Create wrappers for C Binding types (see CBindingWrapping.h).
@@ -1472,6 +1473,7 @@ class MDNode : public Metadata {
LLVM_ABI static MDNode *getMostGenericAliasScope(MDNode *A, MDNode *B);
LLVM_ABI static MDNode *getMostGenericAlignmentOrDereferenceable(MDNode *A,
MDNode *B);
+ LLVM_ABI static MDNode *getMostGenericNoFPClass(MDNode *A, MDNode *B);
/// Merge !prof metadata from two instructions.
/// Currently only implemented with direct callsites with branch weights.
LLVM_ABI static MDNode *getMergedProfMetadata(MDNode *A, MDNode *B,
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 171952120fc40..d6fc0889c3efd 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5098,6 +5098,17 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
.intersectWith(ComputeForArm(Op->getOperand(2), /*Invert=*/true));
break;
}
+ case Instruction::Load: {
+ const MDNode *NoFPClass =
+ cast<LoadInst>(Op)->getMetadata(LLVMContext::MD_nofpclass);
+ if (!NoFPClass)
+ break;
+
+ ConstantInt *MaskVal =
+ mdconst::extract<ConstantInt>(NoFPClass->getOperand(0));
+ Known.knownNot(static_cast<FPClassTest>(MaskVal->getZExtValue()));
+ break;
+ }
case Instruction::Call: {
const CallInst *II = cast<CallInst>(Op);
const Intrinsic::ID IID = II->getIntrinsicID();
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index f4bdf959f05f3..726a3d8dd906f 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -2377,6 +2377,11 @@ AttrBuilder &AttrBuilder::addFromEquivalentMetadata(const Instruction &I) {
if (const MDNode *Range = I.getMetadata(LLVMContext::MD_range))
addRangeAttr(getConstantRangeFromMetadata(*Range));
+ if (const MDNode *NoFPClass = I.getMetadata(LLVMContext::MD_nofpclass)) {
+ ConstantInt *CI = mdconst::extract<ConstantInt>(NoFPClass->getOperand(0));
+ addNoFPClassAttr(static_cast<FPClassTest>(CI->getZExtValue()));
+ }
+
return *this;
}
diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp
index 326abd87ddae9..0410796b24fad 100644
--- a/llvm/lib/IR/Metadata.cpp
+++ b/llvm/lib/IR/Metadata.cpp
@@ -1397,6 +1397,23 @@ MDNode *MDNode::getMostGenericRange(MDNode *A, MDNode *B) {
return MDNode::get(A->getContext(), MDs);
}
+MDNode *MDNode::getMostGenericNoFPClass(MDNode *A, MDNode *B) {
+ if (!A || !B)
+ return nullptr;
+
+ if (A == B)
+ return A;
+
+ ConstantInt *AVal = mdconst::extract<ConstantInt>(A->getOperand(0));
+ ConstantInt *BVal = mdconst::extract<ConstantInt>(B->getOperand(0));
+ unsigned Intersect = AVal->getZExtValue() & BVal->getZExtValue();
+ if (Intersect == 0)
+ return nullptr;
+
+ return MDNode::get(A->getContext(), ConstantAsMetadata::get(ConstantInt::get(
+ AVal->getType(), Intersect)));
+}
+
MDNode *MDNode::getMostGenericNoaliasAddrspace(MDNode *A, MDNode *B) {
if (!A || !B)
return nullptr;
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index b7cfb4ddbd54c..3d44d1317ecc7 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -529,6 +529,7 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
void verifyRangeLikeMetadata(const Value &V, const MDNode *Range, Type *Ty,
RangeLikeMetadataKind Kind);
void visitRangeMetadata(Instruction &I, MDNode *Range, Type *Ty);
+ void visitNoFPClassMetadata(Instruction &I, MDNode *Range, Type *Ty);
void visitNoaliasAddrspaceMetadata(Instruction &I, MDNode *Range, Type *Ty);
void visitDereferenceableMetadata(Instruction &I, MDNode *MD);
void visitNofreeMetadata(Instruction &I, MDNode *MD);
@@ -4570,6 +4571,25 @@ void Verifier::visitRangeMetadata(Instruction &I, MDNode *Range, Type *Ty) {
verifyRangeLikeMetadata(I, Range, Ty, RangeLikeMetadataKind::Range);
}
+void Verifier::visitNoFPClassMetadata(Instruction &I, MDNode *NoFPClass,
+ Type *Ty) {
+ Check(AttributeFuncs::isNoFPClassCompatibleType(Ty),
+ "nofpclass only applies to floating-point typed loads", I);
+
+ Check(NoFPClass->getNumOperands() == 1,
+ "nofpclass must have exactly one entry", NoFPClass);
+ ConstantInt *MaskVal =
+ mdconst::dyn_extract<ConstantInt>(NoFPClass->getOperand(0));
+ Check(MaskVal && MaskVal->getType()->isIntegerTy(32),
+ "nofpclass entry must be a constant i32", NoFPClass);
+ uint32_t Val = MaskVal->getZExtValue();
+ Check(Val != 0, "'nofpclass' must have at least one test bit set", NoFPClass,
+ I);
+
+ Check((Val & ~static_cast<unsigned>(fcAllFlags)) == 0,
+ "Invalid value for 'nofpclass' test mask", NoFPClass, I);
+}
+
void Verifier::visitNoaliasAddrspaceMetadata(Instruction &I, MDNode *Range,
Type *Ty) {
assert(Range && Range == I.getMetadata(LLVMContext::MD_noalias_addrspace) &&
@@ -5692,6 +5712,11 @@ void Verifier::visitInstruction(Instruction &I) {
visitRangeMetadata(I, Range, I.getType());
}
+ if (MDNode *MD = I.getMetadata(LLVMContext::MD_nofpclass)) {
+ Check(isa<LoadInst>(I), "nofpclass is only for loads", &I);
+ visitNoFPClassMetadata(I, MD, I.getType());
+ }
+
if (MDNode *Range = I.getMetadata(LLVMContext::MD_noalias_addrspace)) {
Check(isa<LoadInst>(I) || isa<StoreInst>(I) || isa<AtomicRMWInst>(I) ||
isa<AtomicCmpXchgInst>(I) || isa<CallInst>(I),
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 72e1131a54a86..4a9dcb5e2effd 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -1594,6 +1594,9 @@ void GVNPass::eliminatePartiallyRedundantLoad(
NewLoad->setMetadata(LLVMContext::MD_invariant_group, InvGroupMD);
if (auto *RangeMD = Load->getMetadata(LLVMContext::MD_range))
NewLoad->setMetadata(LLVMContext::MD_range, RangeMD);
+ if (auto *NoFPClassMD = Load->getMetadata(LLVMContext::MD_nofpclass))
+ NewLoad->setMetadata(LLVMContext::MD_nofpclass, NoFPClassMD);
+
if (auto *AccessMD = Load->getMetadata(LLVMContext::MD_access_group))
if (LI->getLoopFor(Load->getParent()) == LI->getLoopFor(UnavailableBlock))
NewLoad->setMetadata(LLVMContext::MD_access_group, AccessMD);
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index f7842a235b20d..ebb00cc5ff9e5 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -2961,6 +2961,10 @@ static void combineMetadata(Instruction *K, const Instruction *J,
if (!AAOnly && (DoesKMove || !K->hasMetadata(LLVMContext::MD_noundef)))
K->setMetadata(Kind, MDNode::getMostGenericRange(JMD, KMD));
break;
+ case LLVMContext::MD_nofpclass:
+ if (!AAOnly && (DoesKMove || !K->hasMetadata(LLVMContext::MD_noundef)))
+ K->setMetadata(Kind, MDNode::getMostGenericNoFPClass(JMD, KMD));
+ break;
case LLVMContext::MD_fpmath:
if (!AAOnly)
K->setMetadata(Kind, MDNode::getMostGenericFPMath(JMD, KMD));
@@ -3149,6 +3153,14 @@ void llvm::copyMetadataForLoad(LoadInst &Dest, const LoadInst &Source) {
case LLVMContext::MD_range:
copyRangeMetadata(DL, Source, N, Dest);
break;
+
+ case LLVMContext::MD_nofpclass:
+ // This only applies if the floating-point type interpretation. This
+ // should handle degenerate cases like casting between a scalar and single
+ // element vector.
+ if (NewType->getScalarType() == Source.getType()->getScalarType())
+ Dest.setMetadata(ID, N);
+ break;
}
}
}
diff --git a/llvm/test/Transforms/ArgumentPromotion/metadata.ll b/llvm/test/Transforms/ArgumentPromotion/metadata.ll
index b3f9fb0c5510e..d340e01365157 100644
--- a/llvm/test/Transforms/ArgumentPromotion/metadata.ll
+++ b/llvm/test/Transforms/ArgumentPromotion/metadata.ll
@@ -3,10 +3,11 @@
declare void @use.i32(i32)
declare void @use.p32(ptr)
+declare void @use.f32(float)
-define internal void @callee(ptr %p1, ptr %p2, ptr %p3, ptr %p4, ptr %p5, ptr %p6, ptr %p7, ptr %p8, ptr %p9, ptr %p10) {
+define internal void @callee(ptr %p1, ptr %p2, ptr %p3, ptr %p4, ptr %p5, ptr %p6, ptr %p7, ptr %p8, ptr %p9, ptr %p10, ptr %p11) {
; CHECK-LABEL: define {{[^@]+}}@callee
-; CHECK-SAME: (i32 [[P1_0_VAL:%.*]], i32 [[P2_0_VAL:%.*]], ptr [[P3_0_VAL:%.*]], ptr [[P4_0_VAL:%.*]], ptr [[P5_0_VAL:%.*]], ptr [[P6_0_VAL:%.*]], ptr [[P7_0_VAL:%.*]], ptr [[P8_0_VAL:%.*]], ptr [[P9_0_VAL:%.*]], ptr [[P10_0_VAL:%.*]]) {
+; CHECK-SAME: (i32 [[P1_0_VAL:%.*]], i32 [[P2_0_VAL:%.*]], ptr [[P3_0_VAL:%.*]], ptr [[P4_0_VAL:%.*]], ptr [[P5_0_VAL:%.*]], ptr [[P6_0_VAL:%.*]], ptr [[P7_0_VAL:%.*]], ptr [[P8_0_VAL:%.*]], ptr [[P9_0_VAL:%.*]], ptr [[P10_0_VAL:%.*]], float [[P11_0_VAL:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[P4_0_VAL]], null
; CHECK-NEXT: call void @llvm.assume(i1 [[TMP1]])
; CHECK-NEXT: call void @use.i32(i32 [[P1_0_VAL]])
@@ -19,6 +20,7 @@ define internal void @callee(ptr %p1, ptr %p2, ptr %p3, ptr %p4, ptr %p5, ptr %p
; CHECK-NEXT: call void @use.p32(ptr [[P8_0_VAL]])
; CHECK-NEXT: call void @use.p32(ptr [[P9_0_VAL]])
; CHECK-NEXT: call void @use.p32(ptr [[P10_0_VAL]])
+; CHECK-NEXT: call void @use.f32(float [[P11_0_VAL]])
; CHECK-NEXT: ret void
;
%v1 = load i32, ptr %p1, !range !0
@@ -31,6 +33,7 @@ define internal void @callee(ptr %p1, ptr %p2, ptr %p3, ptr %p4, ptr %p5, ptr %p
%v8 = load ptr, ptr %p8, !align !3, !noundef !1
%v9 = load ptr, ptr %p9, !noundef !1
%v10 = load ptr, ptr %p10, !nontemporal !4
+ %v11 = load float, ptr %p11, !nofpclass !{i32 3}
call void @use.i32(i32 %v1)
call void @use.i32(i32 %v2)
call void @use.p32(ptr %v3)
@@ -41,26 +44,28 @@ define internal void @callee(ptr %p1, ptr %p2, ptr %p3, ptr %p4, ptr %p5, ptr %p
call void @use.p32(ptr %v8)
call void @use.p32(ptr %v9)
call void @use.p32(ptr %v10)
+ call void @use.f32(float %v11)
ret void
}
-define void @caller(ptr %p1, ptr %p2, ptr %p3, ptr %p4, ptr %p5, ptr %p6, ptr %p7, ptr %p8, ptr %p9, ptr %p10) {
+define void @caller(ptr %p1, ptr %p2, ptr %p3, ptr %p4, ptr %p5, ptr %p6, ptr %p7, ptr %p8, ptr %p9, ptr %p10, ptr %p11) {
; CHECK-LABEL: define {{[^@]+}}@caller
-; CHECK-SAME: (ptr [[P1:%.*]], ptr [[P2:%.*]], ptr [[P3:%.*]], ptr [[P4:%.*]], ptr [[P5:%.*]], ptr [[P6:%.*]], ptr [[P7:%.*]], ptr [[P8:%.*]], ptr [[P9:%.*]], ptr [[P10:%.*]]) {
+; CHECK-SAME: (ptr [[P1:%.*]], ptr [[P2:%.*]], ptr [[P3:%.*]], ptr [[P4:%.*]], ptr [[P5:%.*]], ptr [[P6:%.*]], ptr [[P7:%.*]], ptr [[P8:%.*]], ptr [[P9:%.*]], ptr [[P10:%.*]], ptr [[P11:%.*]]) {
; CHECK-NEXT: [[P1_VAL:%.*]] = load i32, ptr [[P1]], align 4
-; CHECK-NEXT: [[P2_VAL:%.*]] = load i32, ptr [[P2]], align 4, !range [[RNG0:![0-9]+]], !noundef !1
+; CHECK-NEXT: [[P2_VAL:%.*]] = load i32, ptr [[P2]], align 4, !range [[RNG0:![0-9]+]], !noundef [[META1:![0-9]+]]
; CHECK-NEXT: [[P3_VAL:%.*]] = load ptr, ptr [[P3]], align 8
-; CHECK-NEXT: [[P4_VAL:%.*]] = load ptr, ptr [[P4]], align 8, !nonnull !1, !noundef !1
-; CHECK-NEXT: [[P5_VAL:%.*]] = load ptr, ptr [[P5]], align 8, !dereferenceable !2
-; CHECK-NEXT: [[P6_VAL:%.*]] = load ptr, ptr [[P6]], align 8, !dereferenceable_or_null !2
+; CHECK-NEXT: [[P4_VAL:%.*]] = load ptr, ptr [[P4]], align 8, !nonnull [[META1]], !noundef [[META1]]
+; CHECK-NEXT: [[P5_VAL:%.*]] = load ptr, ptr [[P5]], align 8, !dereferenceable [[META2:![0-9]+]]
+; CHECK-NEXT: [[P6_VAL:%.*]] = load ptr, ptr [[P6]], align 8, !dereferenceable_or_null [[META2]]
; CHECK-NEXT: [[P7_VAL:%.*]] = load ptr, ptr [[P7]], align 8
-; CHECK-NEXT: [[P8_VAL:%.*]] = load ptr, ptr [[P8]], align 8, !align !3, !noundef !1
-; CHECK-NEXT: [[P9_VAL:%.*]] = load ptr, ptr [[P9]], align 8, !noundef !1
-; CHECK-NEXT: [[P10_VAL:%.*]] = load ptr, ptr [[P10]], align 8, !nontemporal !4
-; CHECK-NEXT: call void @callee(i32 [[P1_VAL]], i32 [[P2_VAL]], ptr [[P3_VAL]], ptr [[P4_VAL]], ptr [[P5_VAL]], ptr [[P6_VAL]], ptr [[P7_VAL]], ptr [[P8_VAL]], ptr [[P9_VAL]], ptr [[P10_VAL]])
+; CHECK-NEXT: [[P8_VAL:%.*]] = load ptr, ptr [[P8]], align 8, !align [[META3:![0-9]+]], !noundef [[META1]]
+; CHECK-NEXT: [[P9_VAL:%.*]] = load ptr, ptr [[P9]], align 8, !noundef [[META1]]
+; CHECK-NEXT: [[P10_VAL:%.*]] = load ptr, ptr [[P10]], align 8, !nontemporal [[META4:![0-9]+]]
+; CHECK-NEXT: [[P11_VAL:%.*]] = load float, ptr [[P11]], align 4
+; CHECK-NEXT: call void @callee(i32 [[P1_VAL]], i32 [[P2_VAL]], ptr [[P3_VAL]], ptr [[P4_VAL]], ptr [[P5_VAL]], ptr [[P6_VAL]], ptr [[P7_VAL]], ptr [[P8_VAL]], ptr [[P9_VAL]], ptr [[P10_VAL]], float [[P11_VAL]])
; CHECK-NEXT: ret void
;
- call void @callee(ptr %p1, ptr %p2, ptr %p3, ptr %p4, ptr %p5, ptr %p6, ptr %p7, ptr %p8, ptr %p9, ptr %p10)
+ call void @callee(ptr %p1, ptr %p2, ptr %p3, ptr %p4, ptr %p5, ptr %p6, ptr %p7, ptr %p8, ptr %p9, ptr %p10, ptr %p11)
ret void
}
diff --git a/llvm/test/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll
index d325ba001e65c..68b5e80f5419d 100644
--- a/llvm/test/Transforms/Attributor/nofpclass.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass.ll
@@ -3734,6 +3734,62 @@ define float @fadd_double_no_zero_or_sub__output_only_is_ftpz(float noundef nofp
ret float %add
}
+define float @infer_return_from_load_nofpclass_md_f32_0(ptr %ptr) {
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; CHECK-LABEL: define nofpclass(nan) float @infer_return_from_load_nofpclass_md_f32_0
+; CHECK-SAME: (ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[PTR:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[LOAD:%.*]] = load float, ptr [[PTR]], align 4, !nofpclass [[META1:![0-9]+]]
+; CHECK-NEXT: ret float [[LOAD]]
+;
+ %load = load float, ptr %ptr, !nofpclass !1
+ ret float %load
+}
+
+define float @infer_return_from_load_nofpclass_md_f32_1(ptr %ptr) {
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; CHECK-LABEL: define nofpclass(pinf) float @infer_return_from_load_nofpclass_md_f32_1
+; CHECK-SAME: (ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[PTR:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[LOAD:%.*]] = load float, ptr [[PTR]], align 4, !nofpclass [[META2:![0-9]+]]
+; CHECK-NEXT: ret float [[LOAD]]
+;
+ %load = load float, ptr %ptr, !nofpclass !2
+ ret float %load
+}
+
+define <2 x float> @infer_return_from_load_nofpclass_md_v2f32(ptr %ptr) {
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; CHECK-LABEL: define nofpclass(nan) <2 x float> @infer_return_from_load_nofpclass_md_v2f32
+; CHECK-SAME: (ptr nofree noundef nonnull readonly align 8 captures(none) dereferenceable(8) [[PTR:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[LOAD:%.*]] = load <2 x float>, ptr [[PTR]], align 8, !nofpclass [[META1]]
+; CHECK-NEXT: ret <2 x float> [[LOAD]]
+;
+ %load = load <2 x float>, ptr %ptr, !nofpclass !1
+ ret <2 x float> %load
+}
+
+; TODO: Misses this case
+define { float, float } @infer_return_from_load_nofpclass_md_struct(ptr %ptr) {
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; CHECK-LABEL: define { float, float } @infer_return_from_load_nofpclass_md_struct
+; CHECK-SAME: (ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(8) [[PTR:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[LOAD:%.*]] = load { float, float }, ptr [[PTR]], align 4, !nofpclass [[META1]]
+; CHECK-NEXT: ret { float, float } [[LOAD]]
+;
+ %load = load { float, float }, ptr %ptr, !nofpclass !1
+ ret { float, float } %load
+}
+
+define [4 x float] @infer_return_from_load_nofpclass_md_array(ptr %ptr) {
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; CHECK-LABEL: define nofpclass(nan) [4 x float] @infer_return_from_load_nofpclass_md_array
+; CHECK-SAME: (ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(16) [[PTR:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[LOAD:%.*]] = load [4 x float], ptr [[PTR]], align 4, !nofpclass [[META1]]
+; CHECK-NEXT: ret [4 x float] [[LOAD]]
+;
+ %load = load [4 x float], ptr %ptr, !nofpclass !1
+ ret [4 x float] %load
+}
+
declare i64 @_Z13get_global_idj(i32 noundef)
attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
@@ -3748,6 +3804,8 @@ attributes #8 = { "denormal-fp-math"="dynamic,ieee" }
attributes #9 = { "denormal-fp-math"="ieee,dynamic" }
!0 = !{}
+!1 = !{i32 3}
+!2 = !{i32 512}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CGSCC-CI: {{.*}}
diff --git a/llvm/test/Transforms/GVN/PRE/load-metadata.ll b/llvm/test/Transforms/GVN/PRE/load-metadata.ll
index 1128b1be199c2..b09bdf33b8909 100644
--- a/llvm/test/Transforms/GVN/PRE/load-metadata.ll
+++ b/llvm/test/Transforms/GVN/PRE/load-metadata.ll
@@ -45,15 +45,60 @@ block4:
ret i32 %PRE
}
+define float @nofpclass(ptr %p, i1 %C) {
+; MDEP-LABEL: define float @nofpclass(
+; MDEP-SAME: ptr [[P:%.*]], i1 [[C:%.*]]) {
+; MDEP-NEXT: [[BLOCK1:.*:]]
+; MDEP-NEXT: br i1 [[C]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
+; MDEP: [[BLOCK2]]:
+; MDEP-NEXT: [[PRE_PRE:%.*]] = load float, ptr [[P]], align 4, !nofpclass [[META2:![0-9]+]]
+; MDEP-NEXT: br label %[[BLOCK4:.*]]
+; MDEP: [[BLOCK3]]:
+; MDEP-NEXT: store float 0.000000e+00, ptr [[P]], align 4
+; MDEP-NEXT: br label %[[BLOCK4]]
+; MDEP: [[BLOCK4]]:
+; MDEP-NEXT: [[PRE:%.*]] = phi float [ 0.000000e+00, %[[BLOCK3]] ], [ [[PRE_PRE]], %[[BLOCK2]] ]
+; MDEP-NEXT: ret float [[PRE]]
+;
+; MSSA-LABEL: define float @nofpclass(
+; MSSA-SAME: ptr [[P:%.*]], i1 [[C:%.*]]) {
+; MSSA-NEXT: [[BLOCK1:.*:]]
+; MSSA-NEXT: br i1 [[C]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
+; MSSA: [[BLOCK2]]:
+; MSSA-NEXT: br label %[[BLOCK4:.*]]
+; MSSA: [[BLOCK3]]:
+; MSSA-NEXT: store float 0.000000e+00, ptr [[P]], align 4
+; MSSA-NEXT: br label %[[BLOCK4]]
+; MSSA: [[BLOCK4]]:
+; MSSA-NEXT: [[PRE:%.*]] = load float, ptr [[P]], align 4, !nofpclass [[META2:![0-9]+]]
+; MSSA-NEXT: ret float [[PRE]]
+;
+block1:
+ br i1 %C, label %block2, label %block3
+
+block2:
+ br label %block4
+
+block3:
+ store float 0.0, ptr %p
+ br label %block4
+
+block4:
+ %PRE = load float, ptr %p, !nofpclass !{i32 3}
+ ret float %PRE
+}
+
!0 = !{i32 40, i32 100}
!1 = !{!"magic ptr"}
;.
; MDEP: [[RNG0]] = !{i32 40, i32 100}
; MDEP: [[META1]] = !{!"magic ptr"}
+; MDEP: [[META2]] = !{i32 3}
;.
; MSSA: [[RNG0]] = !{i32 40, i32 100}
; MSSA: [[META1]] = !{!"magic ptr"}
+; MSSA: [[META2]] = !{i32 3}
;.
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CHECK: {{.*}}
diff --git a/llvm/test/Transforms/GVN/merge-nofpclass.ll b/llvm/test/Transforms/GVN/merge-nofpclass.ll
new file mode 100644
index 0000000000000..f5b9cd1767e6c
--- /dev/null
+++ b/llvm/test/Transforms/GVN/merge-nofpclass.ll
@@ -0,0 +1,38 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=gvn -S < %s | FileCheck %s
+
+define float @merge_nofpclass(ptr %arg, float %arg1) {
+; CHECK-LABEL: define float @merge_nofpclass(
+; CHECK-SAME: ptr [[ARG:%.*]], float [[ARG1:%.*]]) {
+; CHECK-NEXT: [[BB:.*:]]
+; CHECK-NEXT: [[NOT_INF:%.*]] = load float, ptr [[ARG]], align 4, !nofpclass [[META0:![0-9]+]]
+; CHECK-NEXT: [[ADD:%.*]] = fadd float [[NOT_INF]], [[NOT_INF]]
+; CHECK-NEXT: ret float [[ADD]]
+;
+bb:
+ %not.inf = load float, ptr %arg, align 4, !nofpclass !{i32 519}
+ %not.nan = load float, ptr %arg, align 4, !nofpclass !{i32 3}
+ %add = fadd float %not.nan, %not.inf
+ ret float %add
+}
+
+define float @merge_nofpclass_noundef(ptr %arg, float %arg1) {
+; CHECK-LABEL: define float @merge_nofpclass_noundef(
+; CHECK-SAME: ptr [[ARG:%.*]], float [[ARG1:%.*]]) {
+; CHECK-NEXT: [[BB:.*:]]
+; CHECK-NEXT: [[NOT_INF:%.*]] = load float, ptr [[ARG]], align 4, !noundef [[META1:![0-9]+]], !nofpclass [[META2:![0-9]+]]
+; CHECK-NEXT: [[ADD:%.*]] = fadd float [[NOT_INF]], [[NOT_INF]]
+; CHECK-NEXT: ret float [[ADD]]
+;
+bb:
+ %not.inf = load float, ptr %arg, align 4, !noundef !{}, !nofpclass !{i32 519}
+ %not.nan = load float, ptr %arg, align 4, !nofpclass !{i32 3}
+ %add = fadd float %not.nan, %not.inf
+ ret float %add
+}
+
+;.
+; CHECK: [[META0]] = !{i32 3}
+; CHECK: [[META1]] = !{}
+; CHECK: [[META2]] = !{i32 519}
+;.
diff --git a/llvm/test/Transforms/GVNHoist/hoist-md.ll b/llvm/test/Transforms/GVNHoist/hoist-md.ll
index 2ef9bc30433c3..9e51e845448fc 100644
--- a/llvm/test/Transforms/GVNHoist/hoist-md.ll
+++ b/llvm/test/Transforms/GVNHoist/hoist-md.ll
@@ -157,6 +157,37 @@ return: ; preds = %if.end, %if.then
ret ptr %retval.0
}
+define float @nofpclass_md(i1 %b, ptr %y) {
+; CHECK-LABEL: define float @nofpclass_md(
+; CHECK-SAME: i1 [[B:%.*]], ptr [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[NOT_NAN:%.*]] = load float, ptr [[Y]], align 4, !nofpclass [[META4:![0-9]+]]
+; CHECK-NEXT: br i1 [[B]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
+; CHECK: [[IF_THEN]]:
+; CHECK-NEXT: br label %[[RETURN:.*]]
+; CHECK: [[IF_END]]:
+; CHECK-NEXT: br label %[[RETURN]]
+; CHECK: [[RETURN]]:
+; CHECK-NEXT: [[RETVAL_0:%.*]] = phi float [ [[NOT_NAN]], %[[IF_THEN]] ], [ [[NOT_NAN]], %[[IF_END]] ]
+; CHECK-NEXT: ret float [[RETVAL_0]]
+;
+entry:
+ br i1 %b, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %not.nan = load float, ptr %y, align 4, !nofpclass !{i32 547}
+ br label %return
+
+if.end: ; preds = %entry
+ %not.inf = load float, ptr %y, align 4, !nofpclass !{i32 519}
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %retval.0 = phi float [ %not.nan, %if.then ], [ %not.inf, %if.end ]
+ ret float %retval.0
+}
+
+
!1 = !{!2, !2, i64 0}
!2 = !{!"int", !3, i64 0}
!3 = !{!"omnipotent char", !4, i64 0}
@@ -171,4 +202,5 @@ return: ; preds = %if.end, %if.then
; CHECK: [[META1]] = !{!"omnipotent char", [[META2:![0-9]+]], i64 0}
; CHECK: [[META2]] = !{!"Simple C++ TBAA"}
; CHECK: [[RNG3]] = !{i32 0, i32 2, i32 3, i32 4}
+; CHECK: [[META4]] = !{i32 515}
;.
diff --git a/llvm/test/Transforms/InstCombine/fold-phi-load-metadata.ll b/llvm/test/Transforms/InstCombine/fold-phi-load-metadata.ll
index 6a7e780cf08fe..dc399b67c4a17 100644
--- a/llvm/test/Transforms/InstCombine/fold-phi-load-metadata.ll
+++ b/llvm/test/Transforms/InstCombine/fold-phi-load-metadata.ll
@@ -35,6 +35,29 @@ return: ; preds = %if.end, %if.then
ret i32 %retval
}
+; CHECK-LABEL: define float @phi_load_nofpclass_metadata(
+; CHECK: %retval = load float, ptr %retval.in, align 4, !nofpclass ![[NOFPCLASS:[0-9]+]]
+
+define float @phi_load_nofpclass_metadata(ptr %s1, ptr %s2, i32 %c) #0 {
+entry:
+ %tobool = icmp eq i32 %c, 0
+ br i1 %tobool, label %if.end, label %if.then
+
+if.then: ; preds = %entry
+ %i = getelementptr inbounds %struct.S1, ptr %s2, i64 0, i32 1
+ %val = load float, ptr %i, align 4, !nofpclass !{i32 3}
+ br label %return
+
+if.end: ; preds = %entry
+ %val2 = load float, ptr %s1, align 4, !nofpclass !{i32 519}
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %retval = phi float [ %val, %if.then ], [ %val2, %if.end ]
+ ret float %retval
+}
+
+
; CHECK: ![[EMPTYNODE]] = !{}
; CHECK: ![[TBAA]] = !{![[TAG1:[0-9]+]], ![[TAG1]], i64 0}
; CHECK: ![[TAG1]] = !{!"int", !{{[0-9]+}}, i64 0}
@@ -45,6 +68,7 @@ return: ; preds = %if.end, %if.then
; CHECK: ![[SCOPE2]] = distinct !{![[SCOPE2]], !{{[0-9]+}}, !"scope2"}
; CHECK: ![[NOALIAS]] = !{![[SCOPE3:[0-9]+]]}
; CHECK: ![[SCOPE3]] = distinct !{![[SCOPE3]], !{{[0-9]+}}, !"scope3"}
+; CHECK: ![[NOFPCLASS]] = !{i32 3}
!0 = !{!1, !4, i64 4}
!1 = !{!"", !7, i64 0, !4, i64 4}
diff --git a/llvm/test/Transforms/InstCombine/loadstore-metadata.ll b/llvm/test/Transforms/InstCombine/loadstore-metadata.ll
index 761129979445c..64f9f2797a439 100644
--- a/llvm/test/Transforms/InstCombine/loadstore-metadata.ll
+++ b/llvm/test/Transforms/InstCombine/loadstore-metadata.ll
@@ -327,6 +327,75 @@ entry:
ret double %l.sel
}
+define float @preserve_load_metadata_after_select_transform_nofpclass(i1 %c, ptr dereferenceable(8) %a, ptr dereferenceable(8) %b) {
+; CHECK-LABEL: define float @preserve_load_metadata_after_select_transform_nofpclass(
+; CHECK-SAME: i1 [[C:%.*]], ptr dereferenceable(8) [[A:%.*]], ptr dereferenceable(8) [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[B_VAL:%.*]] = load float, ptr [[B]], align 1, !nofpclass [[META16:![0-9]+]]
+; CHECK-NEXT: [[A_VAL:%.*]] = load float, ptr [[A]], align 1, !nofpclass [[META16]]
+; CHECK-NEXT: [[L_SEL:%.*]] = select i1 [[C]], float [[B_VAL]], float [[A_VAL]]
+; CHECK-NEXT: ret float [[L_SEL]]
+;
+entry:
+ %ptr.sel = select i1 %c, ptr %b, ptr %a
+ %l.sel = load float, ptr %ptr.sel, align 1, !nofpclass !{i32 3}
+ ret float %l.sel
+}
+
+; nofpclass should be dropped
+define i32 @test_load_cast_combine_nofpclass_int(ptr %ptr) {
+; CHECK-LABEL: define i32 @test_load_cast_combine_nofpclass_int(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[L1:%.*]] = load i32, ptr [[PTR]], align 4
+; CHECK-NEXT: ret i32 [[L1]]
+;
+entry:
+ %l = load float, ptr %ptr, !nofpclass !{i32 3}
+ %c = bitcast float %l to i32
+ ret i32 %c
+}
+
+; nofpclass should be dropped
+define <2 x half> @test_load_cast_combine_nofpclass_fpvec(ptr %ptr) {
+; CHECK-LABEL: define <2 x half> @test_load_cast_combine_nofpclass_fpvec(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[L1:%.*]] = load <2 x half>, ptr [[PTR]], align 4
+; CHECK-NEXT: ret <2 x half> [[L1]]
+;
+entry:
+ %l = load float, ptr %ptr, !nofpclass !{i32 3}
+ %c = bitcast float %l to <2 x half>
+ ret <2 x half> %c
+}
+
+define float @test_load_cast_combine_nofpclass_1xvec_to_scalar(ptr %ptr) {
+; CHECK-LABEL: define float @test_load_cast_combine_nofpclass_1xvec_to_scalar(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[L1:%.*]] = load float, ptr [[PTR]], align 4, !nofpclass [[META16]]
+; CHECK-NEXT: ret float [[L1]]
+;
+entry:
+ %l = load <1 x float>, ptr %ptr, !nofpclass !{i32 3}
+ %c = bitcast <1 x float> %l to float
+ ret float %c
+}
+
+define <1 x float> @test_load_cast_combine_nofpclass_scalar_to_1xvec(ptr %ptr) {
+; CHECK-LABEL: define <1 x float> @test_load_cast_combine_nofpclass_scalar_to_1xvec(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[L1:%.*]] = load <1 x float>, ptr [[PTR]], align 4, !nofpclass [[META16]]
+; CHECK-NEXT: ret <1 x float> [[L1]]
+;
+entry:
+ %l = load float, ptr %ptr, !nofpclass !{i32 3}
+ %c = bitcast float %l to <1 x float>
+ ret <1 x float> %c
+}
+
!0 = !{!1, !1, i64 0}
!1 = !{!"scalar type", !2}
!2 = !{!"root"}
@@ -362,4 +431,5 @@ entry:
; CHECK: [[META13]] = distinct !{[[META13]], [[META14:![0-9]+]]}
; CHECK: [[META14]] = distinct !{[[META14]]}
; CHECK: [[ACC_GRP15]] = distinct !{}
+; CHECK: [[META16]] = !{i32 3}
;.
diff --git a/llvm/test/Transforms/SROA/preserve-metadata.ll b/llvm/test/Transforms/SROA/preserve-metadata.ll
index ba8fbc8efc375..8368e9e2c0c6e 100644
--- a/llvm/test/Transforms/SROA/preserve-metadata.ll
+++ b/llvm/test/Transforms/SROA/preserve-metadata.ll
@@ -146,6 +146,24 @@ define i128 @load_i128_to_load_ptr() {
ret i128 %v
}
+define float @propagate_nofpclass(float %v) {
+; CHECK-LABEL: @propagate_nofpclass(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A_SROA_1:%.*]] = alloca float, align 4
+; CHECK-NEXT: store float [[V:%.*]], ptr [[A_SROA_1]], align 4
+; CHECK-NEXT: [[A_SROA_1_0_A_SROA_1_4_LOAD:%.*]] = load volatile float, ptr [[A_SROA_1]], align 4, !nofpclass [[META2:![0-9]+]]
+; CHECK-NEXT: ret float [[A_SROA_1_0_A_SROA_1_4_LOAD]]
+;
+entry:
+ %a = alloca [2 x float]
+ %a.gep1 = getelementptr [2 x float], ptr %a, i32 0, i32 1
+ store float %v, ptr %a.gep1
+ store float 0.0, ptr %a
+ %load = load volatile float, ptr %a.gep1, !nofpclass !{i32 3}
+ ret float %load
+}
+
+
!0 = !{}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CHECK-MODIFY-CFG: {{.*}}
diff --git a/llvm/test/Transforms/SimplifyCFG/hoist-with-metadata.ll b/llvm/test/Transforms/SimplifyCFG/hoist-with-metadata.ll
index 85c8ed20210b8..26e87393e2e7b 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-with-metadata.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-with-metadata.ll
@@ -592,6 +592,49 @@ out:
ret void
}
+define void @hoist_nofpclass_intersect(i1 %c, ptr %p) {
+; CHECK-LABEL: @hoist_nofpclass_intersect(
+; CHECK-NEXT: if:
+; CHECK-NEXT: [[T:%.*]] = load float, ptr [[P:%.*]], align 4, !nofpclass [[META12:![0-9]+]]
+; CHECK-NEXT: ret void
+;
+if:
+ br i1 %c, label %then, label %else
+
+then:
+ %t = load float, ptr %p, !nofpclass !{i32 3} ; nan
+ br label %out
+
+else:
+ %e = load float, ptr %p, !nofpclass !{i32 519} ; inf nan
+ br label %out
+
+out:
+ ret void
+}
+
+define void @hoist_nofpclass_drop(i1 %c, ptr %p) {
+; CHECK-LABEL: @hoist_nofpclass_drop(
+; CHECK-NEXT: if:
+; CHECK-NEXT: [[T:%.*]] = load float, ptr [[P:%.*]], align 4
+; CHECK-NEXT: ret void
+;
+if:
+ br i1 %c, label %then, label %else
+
+then:
+ %t = load float, ptr %p, !nofpclass !{i32 3} ; nan
+ br label %out
+
+else:
+ %e = load float, ptr %p, !nofpclass !{i32 512} ; inf
+ br label %out
+
+out:
+ ret void
+}
+
+
!0 = !{ i8 0, i8 1 }
!1 = !{ i8 3, i8 5 }
!2 = !{}
@@ -616,4 +659,5 @@ out:
; CHECK: [[META9]] = !{!"address"}
; CHECK: [[META10]] = !{!"address", !"read_provenance"}
; CHECK: [[META11]] = !{!"provenance"}
+; CHECK: [[META12]] = !{i32 3}
;.
diff --git a/llvm/test/Verifier/nofpclass-metadata.ll b/llvm/test/Verifier/nofpclass-metadata.ll
new file mode 100644
index 0000000000000..09b30ea9c7908
--- /dev/null
+++ b/llvm/test/Verifier/nofpclass-metadata.ll
@@ -0,0 +1,91 @@
+; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s
+
+; CHECK: nofpclass must have exactly one entry
+; CHECK-NEXT: !0 = !{}
+define float @md_missing_value(ptr %ptr) {
+ %load = load float, ptr %ptr, align 4, !nofpclass !{}
+ ret float %load
+}
+
+; CHECK: nofpclass must have exactly one entry
+; CHECK-NEXT: !1 = !{i32 1, i32 2}
+define float @md_too_many_entries(ptr %ptr) {
+ %load = load float, ptr %ptr, align 4, !nofpclass !{i32 1, i32 2}
+ ret float %load
+}
+
+; CHECK: nofpclass entry must be a constant i32
+; CHECK: !2 = !{i64 1}
+define float @md_is_i64(ptr %ptr) {
+ %load = load float, ptr %ptr, align 4, !nofpclass !{i64 1}
+ ret float %load
+}
+
+; CHECK: nofpclass entry must be a constant i32
+; CHECK-NEXT: !3 = !{float 1.000000e+00}
+define float @md_is_float(ptr %ptr) {
+ %load = load float, ptr %ptr, align 4, !nofpclass !{float 1.0}
+ ret float %load
+}
+
+; CHECK: nofpclass entry must be a constant i32
+; CHECK-NEXT: !4 = !{!"foo"}
+define float @md_is_string(ptr %ptr) {
+ %load = load float, ptr %ptr, align 4, !nofpclass !{!"foo"}
+ ret float %load
+}
+
+; CHECK: nofpclass entry must be a constant i32
+; CHECK-NEXT: !5 = !{ptr @md_is_ptr}
+define float @md_is_ptr(ptr %ptr) {
+ %load = load float, ptr %ptr, align 4, !nofpclass !{ptr @md_is_ptr}
+ ret float %load
+}
+
+; CHECK: 'nofpclass' must have at least one test bit set
+; CHECK-NEXT: !6 = !{i32 0}
+; CHECK-NEXT: %load = load float, ptr %ptr, align 4, !nofpclass !6
+ define float @md_is_zero_val(ptr %ptr) {
+ %load = load float, ptr %ptr, align 4, !nofpclass !{i32 0}
+ ret float %load
+}
+
+; CHECK: Invalid value for 'nofpclass' test mask
+; CHECK-NEXT: !7 = !{i32 1024}
+; CHECK-NEXT: %load = load float, ptr %ptr, align 4, !nofpclass !7
+define float @md_is_out_of_bounds(ptr %ptr) {
+ %load = load float, ptr %ptr, align 4, !nofpclass !{i32 1024}
+ ret float %load
+}
+
+declare float @func()
+
+; CHECK: nofpclass is only for loads
+; CHECK-NEXT: %result = call float @func(), !nofpclass !8
+define float @not_load(ptr %ptr) {
+ %result = call float @func(), !nofpclass !{i32 3}
+ ret float %result
+}
+
+; CHECK: nofpclass only applies to floating-point typed loads
+; CHECK-NEXT: %load = load i32, ptr %ptr, align 4, !nofpclass !8
+define i32 @load_int(ptr %ptr) {
+ %load = load i32, ptr %ptr, align 4, !nofpclass !{i32 3}
+ ret i32 %load
+}
+
+; CHECK: nofpclass only applies to floating-point typed loads
+; CHECK-NEXT: %load = load <2 x i32>, ptr %ptr, align 8, !nofpclass !8
+define <2 x i32> @load_int_vec(ptr %ptr) {
+ %load = load <2 x i32>, ptr %ptr, align 8, !nofpclass !{i32 3}
+ ret <2 x i32> %load
+}
+
+%struct = type { i32, float }
+
+; CHECK: nofpclass only applies to floating-point typed loads
+; CHECK-NEXT: %load = load %struct, ptr %ptr, align 4, !nofpclass !8
+define %struct @load_hetero_struct(ptr %ptr) {
+ %load = load %struct, ptr %ptr, align 4, !nofpclass !{i32 3}
+ ret %struct %load
+}
diff --git a/llvm/test/tools/llvm-reduce/reduce-operands-to-args-metadata-to-attributes.ll b/llvm/test/tools/llvm-reduce/reduce-operands-to-args-metadata-to-attributes.ll
index 913ba9d3218fd..971d1c349d262 100644
--- a/llvm/test/tools/llvm-reduce/reduce-operands-to-args-metadata-to-attributes.ll
+++ b/llvm/test/tools/llvm-reduce/reduce-operands-to-args-metadata-to-attributes.ll
@@ -66,7 +66,15 @@ define void @use_noundef_range() {
ret void
}
-
+; INTERESTING-LABEL: define void @use_nofpclass(
+; REDUCED-LABEL: define void @use_nofpclass(float nofpclass(nan) %load, <2 x float> nofpclass(inf) %load_vec) {
+define void @use_nofpclass() {
+ %load = load float, ptr null, !nofpclass !{i32 3}
+ %load_vec = load <2 x float>, ptr null, !nofpclass !{i32 516}
+ store float %load, ptr null
+ store <2 x float> %load_vec, ptr null
+ ret void
+}
!0 = !{}
!1 = !{i64 16}
More information about the llvm-commits
mailing list