[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