Index: llvm/include/llvm/ADT/APFloat.h =================================================================== --- llvm/include/llvm/ADT/APFloat.h +++ llvm/include/llvm/ADT/APFloat.h @@ -393,6 +393,10 @@ /// magnitude in the current semantics. bool isSmallest() const; + /// Returns true if this is the smallest (by magnitude) normalized finite + /// number in the given semantics. + bool isSmallestNormalized() const; + /// Returns true if and only if the number has the largest possible finite /// magnitude in the current semantics. bool isLargest() const; @@ -517,6 +521,7 @@ bool isSignificandAllOnesExceptLSB() const; /// Return true if the significand excluding the integral bit is all zeros. bool isSignificandAllZeros() const; + bool isSignificandAllZerosExceptMSB() const; /// @} @@ -694,6 +699,7 @@ bool isDenormal() const; bool isSmallest() const; + bool isSmallestNormalized() const; bool isLargest() const; bool isInteger() const; @@ -1247,6 +1253,10 @@ bool isInteger() const { APFLOAT_DISPATCH_ON_SEMANTICS(isInteger()); } bool isIEEE() const { return usesLayout(getSemantics()); } + bool isSmallestNormalized() const { + APFLOAT_DISPATCH_ON_SEMANTICS(isSmallestNormalized()); + } + APFloat &operator=(const APFloat &RHS) = default; APFloat &operator=(APFloat &&RHS) = default; Index: llvm/lib/Support/APFloat.cpp =================================================================== --- llvm/lib/Support/APFloat.cpp +++ llvm/lib/Support/APFloat.cpp @@ -883,6 +883,11 @@ significandMSB() == 0; } +bool IEEEFloat::isSmallestNormalized() const { + return getCategory() == fcNormal && exponent == semantics->minExponent && + isSignificandAllZerosExceptMSB(); +} + bool IEEEFloat::isSignificandAllOnes() const { // Test if the significand excluding the integral bit is all ones. This allows // us to test for binade boundaries. @@ -955,6 +960,21 @@ return true; } +bool IEEEFloat::isSignificandAllZerosExceptMSB() const { + const integerPart *Parts = significandParts(); + const unsigned PartCount = partCountForBits(semantics->precision); + + for (unsigned i = 0; i < PartCount - 1; i++) { + if (Parts[i]) + return false; + } + + const unsigned NumHighBits = + PartCount * integerPartWidth - semantics->precision + 1; + return Parts[PartCount - 1] == integerPart(1) + << (integerPartWidth - NumHighBits); +} + bool IEEEFloat::isLargest() const { if (semantics->nonFiniteBehavior == fltNonfiniteBehavior::NanOnly) { // The largest number by magnitude in our format will be the floating point @@ -4998,6 +5018,15 @@ return Tmp.compare(*this) == cmpEqual; } +bool DoubleAPFloat::isSmallestNormalized() const { + if (getCategory() != fcNormal) + return false; + + DoubleAPFloat Tmp(*this); + Tmp.makeSmallestNormalized(this->isNegative()); + return Tmp.compare(*this) == cmpEqual; +} + bool DoubleAPFloat::isLargest() const { if (getCategory() != fcNormal) return false; Index: llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -6780,7 +6780,7 @@ return nullptr; if (!C->isPosZero()) { - if (*C != APFloat::getSmallestNormalized(C->getSemantics())) + if (!C->isSmallestNormalized()) return nullptr; const Function *F = I.getFunction(); Index: llvm/test/Transforms/InstCombine/is_fpclass.ll =================================================================== --- llvm/test/Transforms/InstCombine/is_fpclass.ll +++ llvm/test/Transforms/InstCombine/is_fpclass.ll @@ -12,7 +12,7 @@ ; } define i1 @test_class_no_mask_f32(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_no_mask_f32( +; CHECK-LABEL: @test_class_no_mask_f32( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f32(float %x, i32 0) @@ -20,7 +20,7 @@ } define i1 @test_class_full_mask_f32(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_full_mask_f32( +; CHECK-LABEL: @test_class_full_mask_f32( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f32(float %x, i32 1023) @@ -28,7 +28,7 @@ } define i1 @test_class_undef_no_mask_f32() { -; CHECK-LABEL: define {{[^@]+}}@test_class_undef_no_mask_f32( +; CHECK-LABEL: @test_class_undef_no_mask_f32( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f32(float undef, i32 0) @@ -36,7 +36,7 @@ } define i1 @test_class_undef_full_mask_f32() { -; CHECK-LABEL: define {{[^@]+}}@test_class_undef_full_mask_f32( +; CHECK-LABEL: @test_class_undef_full_mask_f32( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f32(float undef, i32 1023) @@ -44,7 +44,7 @@ } define i1 @test_class_poison_no_mask_f32() { -; CHECK-LABEL: define {{[^@]+}}@test_class_poison_no_mask_f32( +; CHECK-LABEL: @test_class_poison_no_mask_f32( ; CHECK-NEXT: ret i1 poison ; %val = call i1 @llvm.is.fpclass.f32(float poison, i32 0) @@ -52,7 +52,7 @@ } define i1 @test_class_poison_full_mask_f32() { -; CHECK-LABEL: define {{[^@]+}}@test_class_poison_full_mask_f32( +; CHECK-LABEL: @test_class_poison_full_mask_f32( ; CHECK-NEXT: ret i1 poison ; %val = call i1 @llvm.is.fpclass.f32(float poison, i32 1023) @@ -60,7 +60,7 @@ } define i1 @test_class_undef_val_f32() { -; CHECK-LABEL: define {{[^@]+}}@test_class_undef_val_f32( +; CHECK-LABEL: @test_class_undef_val_f32( ; CHECK-NEXT: ret i1 undef ; %val = call i1 @llvm.is.fpclass.f32(float undef, i32 4) @@ -68,7 +68,7 @@ } define i1 @test_class_poison_val_f32() { -; CHECK-LABEL: define {{[^@]+}}@test_class_poison_val_f32( +; CHECK-LABEL: @test_class_poison_val_f32( ; CHECK-NEXT: ret i1 poison ; %val = call i1 @llvm.is.fpclass.f32(float poison, i32 4) @@ -88,7 +88,7 @@ ; } define i1 @test_class_isnan_f32(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_isnan_f32( +; CHECK-LABEL: @test_class_isnan_f32( ; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 3) ; CHECK-NEXT: ret i1 [[VAL]] ; @@ -97,7 +97,7 @@ } define i1 @test_class_isnan_f32_strict(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_isnan_f32_strict( +; CHECK-LABEL: @test_class_isnan_f32_strict( ; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 3) #[[ATTR1:[0-9]+]] ; CHECK-NEXT: ret i1 [[VAL]] ; @@ -106,7 +106,7 @@ } define i1 @test_class_is_p0_n0_f32(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_is_p0_n0_f32( +; CHECK-LABEL: @test_class_is_p0_n0_f32( ; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 96) ; CHECK-NEXT: ret i1 [[VAL]] ; @@ -115,7 +115,7 @@ } define i1 @test_class_is_p0_n0_f32_strict(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_is_p0_n0_f32_strict( +; CHECK-LABEL: @test_class_is_p0_n0_f32_strict( ; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 96) #[[ATTR1]] ; CHECK-NEXT: ret i1 [[VAL]] ; @@ -124,7 +124,7 @@ } define i1 @test_constant_class_snan_test_snan_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_snan_test_snan_f64( +; CHECK-LABEL: @test_constant_class_snan_test_snan_f64( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f64(double 0x7FF0000000000001, i32 1) @@ -132,7 +132,7 @@ } define i1 @test_constant_class_qnan_test_qnan_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_qnan_test_qnan_f64( +; CHECK-LABEL: @test_constant_class_qnan_test_qnan_f64( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f64(double 0x7FF8000000000000, i32 2) @@ -140,7 +140,7 @@ } define i1 @test_constant_class_qnan_test_snan_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_qnan_test_snan_f64( +; CHECK-LABEL: @test_constant_class_qnan_test_snan_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 0x7FF8000000000000, i32 1) @@ -148,7 +148,7 @@ } define i1 @test_constant_class_ninf_test_ninf_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_ninf_test_ninf_f64( +; CHECK-LABEL: @test_constant_class_ninf_test_ninf_f64( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f64(double 0xFFF0000000000000, i32 4) @@ -156,7 +156,7 @@ } define i1 @test_constant_class_pinf_test_ninf_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_pinf_test_ninf_f64( +; CHECK-LABEL: @test_constant_class_pinf_test_ninf_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 0x7FF0000000000000, i32 4) @@ -164,7 +164,7 @@ } define i1 @test_constant_class_qnan_test_ninf_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_qnan_test_ninf_f64( +; CHECK-LABEL: @test_constant_class_qnan_test_ninf_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 0x7FF8000000000000, i32 4) @@ -172,7 +172,7 @@ } define i1 @test_constant_class_snan_test_ninf_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_snan_test_ninf_f64( +; CHECK-LABEL: @test_constant_class_snan_test_ninf_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 0x7FF0000000000001, i32 4) @@ -180,7 +180,7 @@ } define i1 @test_constant_class_nnormal_test_nnormal_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_nnormal_test_nnormal_f64( +; CHECK-LABEL: @test_constant_class_nnormal_test_nnormal_f64( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f64(double -1.0, i32 8) @@ -188,7 +188,7 @@ } define i1 @test_constant_class_pnormal_test_nnormal_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_pnormal_test_nnormal_f64( +; CHECK-LABEL: @test_constant_class_pnormal_test_nnormal_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 1.0, i32 8) @@ -196,7 +196,7 @@ } define i1 @test_constant_class_nsubnormal_test_nsubnormal_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_nsubnormal_test_nsubnormal_f64( +; CHECK-LABEL: @test_constant_class_nsubnormal_test_nsubnormal_f64( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f64(double 0x800fffffffffffff, i32 16) @@ -204,7 +204,7 @@ } define i1 @test_constant_class_psubnormal_test_nsubnormal_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_psubnormal_test_nsubnormal_f64( +; CHECK-LABEL: @test_constant_class_psubnormal_test_nsubnormal_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 0x000fffffffffffff, i32 16) @@ -212,7 +212,7 @@ } define i1 @test_constant_class_nzero_test_nzero_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_nzero_test_nzero_f64( +; CHECK-LABEL: @test_constant_class_nzero_test_nzero_f64( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f64(double -0.0, i32 32) @@ -220,7 +220,7 @@ } define i1 @test_constant_class_pzero_test_nzero_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_pzero_test_nzero_f64( +; CHECK-LABEL: @test_constant_class_pzero_test_nzero_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 0.0, i32 32) @@ -228,7 +228,7 @@ } define i1 @test_constant_class_pzero_test_pzero_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_pzero_test_pzero_f64( +; CHECK-LABEL: @test_constant_class_pzero_test_pzero_f64( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f64(double 0.0, i32 64) @@ -236,7 +236,7 @@ } define i1 @test_constant_class_nzero_test_pzero_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_nzero_test_pzero_f64( +; CHECK-LABEL: @test_constant_class_nzero_test_pzero_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double -0.0, i32 64) @@ -244,7 +244,7 @@ } define i1 @test_constant_class_psubnormal_test_psubnormal_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_psubnormal_test_psubnormal_f64( +; CHECK-LABEL: @test_constant_class_psubnormal_test_psubnormal_f64( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f64(double 0x000fffffffffffff, i32 128) @@ -252,7 +252,7 @@ } define i1 @test_constant_class_nsubnormal_test_psubnormal_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_nsubnormal_test_psubnormal_f64( +; CHECK-LABEL: @test_constant_class_nsubnormal_test_psubnormal_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 0x800fffffffffffff, i32 128) @@ -260,7 +260,7 @@ } define i1 @test_constant_class_pnormal_test_pnormal_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_pnormal_test_pnormal_f64( +; CHECK-LABEL: @test_constant_class_pnormal_test_pnormal_f64( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f64(double 1.0, i32 256) @@ -268,7 +268,7 @@ } define i1 @test_constant_class_nnormal_test_pnormal_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_nnormal_test_pnormal_f64( +; CHECK-LABEL: @test_constant_class_nnormal_test_pnormal_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double -1.0, i32 256) @@ -276,7 +276,7 @@ } define i1 @test_constant_class_pinf_test_pinf_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_pinf_test_pinf_f64( +; CHECK-LABEL: @test_constant_class_pinf_test_pinf_f64( ; CHECK-NEXT: ret i1 true ; %val = call i1 @llvm.is.fpclass.f64(double 0x7FF0000000000000, i32 512) @@ -284,7 +284,7 @@ } define i1 @test_constant_class_ninf_test_pinf_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_ninf_test_pinf_f64( +; CHECK-LABEL: @test_constant_class_ninf_test_pinf_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 0xFFF0000000000000, i32 512) @@ -292,7 +292,7 @@ } define i1 @test_constant_class_qnan_test_pinf_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_qnan_test_pinf_f64( +; CHECK-LABEL: @test_constant_class_qnan_test_pinf_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 0x7FF8000000000000, i32 512) @@ -300,7 +300,7 @@ } define i1 @test_constant_class_snan_test_pinf_f64() { -; CHECK-LABEL: define {{[^@]+}}@test_constant_class_snan_test_pinf_f64( +; CHECK-LABEL: @test_constant_class_snan_test_pinf_f64( ; CHECK-NEXT: ret i1 false ; %val = call i1 @llvm.is.fpclass.f64(double 0x7FF0000000000001, i32 512) @@ -308,7 +308,7 @@ } define i1 @test_class_is_snan_nnan_src(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_is_snan_nnan_src( +; CHECK-LABEL: @test_class_is_snan_nnan_src( ; CHECK-NEXT: [[NNAN:%.*]] = fadd nnan float [[X:%.*]], 1.000000e+00 ; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NNAN]], i32 1) ; CHECK-NEXT: ret i1 [[CLASS]] @@ -319,7 +319,7 @@ } define i1 @test_class_is_qnan_nnan_src(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_is_qnan_nnan_src( +; CHECK-LABEL: @test_class_is_qnan_nnan_src( ; CHECK-NEXT: [[NNAN:%.*]] = fadd nnan float [[X:%.*]], 1.000000e+00 ; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NNAN]], i32 2) ; CHECK-NEXT: ret i1 [[CLASS]] @@ -330,7 +330,7 @@ } define i1 @test_class_is_nan_nnan_src(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_is_nan_nnan_src( +; CHECK-LABEL: @test_class_is_nan_nnan_src( ; CHECK-NEXT: [[NNAN:%.*]] = fadd nnan float [[X:%.*]], 1.000000e+00 ; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NNAN]], i32 3) ; CHECK-NEXT: ret i1 [[CLASS]] @@ -341,7 +341,7 @@ } define i1 @test_class_is_nan_other_nnan_src(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_is_nan_other_nnan_src( +; CHECK-LABEL: @test_class_is_nan_other_nnan_src( ; CHECK-NEXT: [[NNAN:%.*]] = fadd nnan float [[X:%.*]], 1.000000e+00 ; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NNAN]], i32 267) ; CHECK-NEXT: ret i1 [[CLASS]] @@ -356,7 +356,7 @@ ; -------------------------------------------------------------------- define i1 @test_class_not_is_nan(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_not_is_nan( +; CHECK-LABEL: @test_class_not_is_nan( ; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 1020) ; CHECK-NEXT: ret i1 [[CLASS]] ; @@ -366,7 +366,7 @@ } define i1 @test_class_not_is_nan_multi_use(float %x, ptr %ptr) { -; CHECK-LABEL: define {{[^@]+}}@test_class_not_is_nan_multi_use( +; CHECK-LABEL: @test_class_not_is_nan_multi_use( ; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 3) ; CHECK-NEXT: store i1 [[CLASS]], ptr [[PTR:%.*]], align 1 ; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[CLASS]], true @@ -379,7 +379,7 @@ } define i1 @test_class_not_is_inf_nan(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_not_is_inf_nan( +; CHECK-LABEL: @test_class_not_is_inf_nan( ; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 504) ; CHECK-NEXT: ret i1 [[CLASS]] ; @@ -389,7 +389,7 @@ } define i1 @test_class_not_is_normal(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_not_is_normal( +; CHECK-LABEL: @test_class_not_is_normal( ; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 759) ; CHECK-NEXT: ret i1 [[CLASS]] ; @@ -399,7 +399,7 @@ } define i1 @test_class_xor_false(float %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_xor_false( +; CHECK-LABEL: @test_class_xor_false( ; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 33) ; CHECK-NEXT: ret i1 [[CLASS]] ; @@ -409,7 +409,7 @@ } define <2 x i1> @test_class_not_vector(<2 x float> %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_not_vector( +; CHECK-LABEL: @test_class_not_vector( ; CHECK-NEXT: [[CLASS:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[X:%.*]], i32 990) ; CHECK-NEXT: ret <2 x i1> [[CLASS]] ; @@ -419,7 +419,7 @@ } define <2 x i1> @test_class_xor_vector(<2 x float> %x) { -; CHECK-LABEL: define {{[^@]+}}@test_class_xor_vector( +; CHECK-LABEL: @test_class_xor_vector( ; CHECK-NEXT: [[CLASS:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[X:%.*]], i32 33) ; CHECK-NEXT: [[NOT:%.*]] = xor <2 x i1> [[CLASS]], ; CHECK-NEXT: ret <2 x i1> [[NOT]] @@ -434,7 +434,7 @@ ; -------------------------------------------------------------------- define i1 @test_fold_or_class_f32_0(float %a) { -; CHECK-LABEL: define {{[^@]+}}@test_fold_or_class_f32_0( +; CHECK-LABEL: @test_fold_or_class_f32_0( ; CHECK-NEXT: [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 1) ; CHECK-NEXT: [[CLASS1:%.*]] = fcmp uno float [[A]], 0.000000e+00 ; CHECK-NEXT: [[OR:%.*]] = or i1 [[CLASS0]], [[CLASS1]] @@ -447,7 +447,7 @@ } define i1 @test_fold_or3_class_f32_0(float %a) { -; CHECK-LABEL: define {{[^@]+}}@test_fold_or3_class_f32_0( +; CHECK-LABEL: @test_fold_or3_class_f32_0( ; CHECK-NEXT: [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 1) ; CHECK-NEXT: [[CLASS1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 2) ; CHECK-NEXT: [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 4) @@ -464,7 +464,7 @@ } define i1 @test_fold_or_all_tests_class_f32_0(float %a) { -; CHECK-LABEL: define {{[^@]+}}@test_fold_or_all_tests_class_f32_0( +; CHECK-LABEL: @test_fold_or_all_tests_class_f32_0( ; CHECK-NEXT: [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 1) ; CHECK-NEXT: [[CLASS1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 2) ; CHECK-NEXT: [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 4) @@ -509,7 +509,7 @@ } define i1 @test_fold_or_class_f32_1(float %a) { -; CHECK-LABEL: define {{[^@]+}}@test_fold_or_class_f32_1( +; CHECK-LABEL: @test_fold_or_class_f32_1( ; CHECK-NEXT: [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 4) ; CHECK-NEXT: [[CLASS1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 8) ; CHECK-NEXT: [[OR:%.*]] = or i1 [[CLASS0]], [[CLASS1]] @@ -522,7 +522,7 @@ } define i1 @test_no_fold_or_class_f32_multi_use0(float %a, ptr %ptr) { -; CHECK-LABEL: define {{[^@]+}}@test_no_fold_or_class_f32_multi_use0( +; CHECK-LABEL: @test_no_fold_or_class_f32_multi_use0( ; CHECK-NEXT: [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 4) ; CHECK-NEXT: store i1 [[CLASS0]], ptr [[PTR:%.*]], align 1 ; CHECK-NEXT: [[CLASS1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 8) @@ -537,7 +537,7 @@ } define i1 @test_no_fold_or_class_f32_multi_use1(float %a, ptr %ptr) { -; CHECK-LABEL: define {{[^@]+}}@test_no_fold_or_class_f32_multi_use1( +; CHECK-LABEL: @test_no_fold_or_class_f32_multi_use1( ; CHECK-NEXT: [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 4) ; CHECK-NEXT: [[CLASS1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 8) ; CHECK-NEXT: store i1 [[CLASS1]], ptr [[PTR:%.*]], align 1 @@ -552,7 +552,7 @@ } define i1 @test_fold_or_class_f32_2(float %a) { -; CHECK-LABEL: define {{[^@]+}}@test_fold_or_class_f32_2( +; CHECK-LABEL: @test_fold_or_class_f32_2( ; CHECK-NEXT: [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 7) ; CHECK-NEXT: [[CLASS1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 7) ; CHECK-NEXT: [[OR:%.*]] = or i1 [[CLASS0]], [[CLASS1]] @@ -565,7 +565,7 @@ } define i1 @test_no_fold_or_class_f32_0(float %a, float %b) { -; CHECK-LABEL: define {{[^@]+}}@test_no_fold_or_class_f32_0( +; CHECK-LABEL: @test_no_fold_or_class_f32_0( ; CHECK-NEXT: [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 4) ; CHECK-NEXT: [[CLASS1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[B:%.*]], i32 8) ; CHECK-NEXT: [[OR:%.*]] = or i1 [[CLASS0]], [[CLASS1]] @@ -578,7 +578,7 @@ } define <2 x i1> @test_fold_or_class_v2f32(<2 x float> %a) { -; CHECK-LABEL: define {{[^@]+}}@test_fold_or_class_v2f32( +; CHECK-LABEL: @test_fold_or_class_v2f32( ; CHECK-NEXT: [[CLASS0:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[A:%.*]], i32 4) ; CHECK-NEXT: [[CLASS1:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[A]], i32 8) ; CHECK-NEXT: [[OR:%.*]] = or <2 x i1> [[CLASS0]], [[CLASS1]] Index: llvm/unittests/ADT/APFloatTest.cpp =================================================================== --- llvm/unittests/ADT/APFloatTest.cpp +++ llvm/unittests/ADT/APFloatTest.cpp @@ -664,6 +664,57 @@ } } +TEST(APFloatTest, IsSmallestNormalized) { + for (unsigned I = 0; I != APFloat::S_MaxSemantics + 1; ++I) { + const fltSemantics &Semantics = + APFloat::EnumToSemantics(static_cast(I)); + + EXPECT_FALSE(APFloat::getZero(Semantics, false).isSmallestNormalized()); + EXPECT_FALSE(APFloat::getZero(Semantics, true).isSmallestNormalized()); + + EXPECT_FALSE(APFloat::getInf(Semantics, false).isSmallestNormalized()); + EXPECT_FALSE(APFloat::getInf(Semantics, true).isSmallestNormalized()); + + EXPECT_FALSE(APFloat::getQNaN(Semantics).isSmallestNormalized()); + EXPECT_FALSE(APFloat::getSNaN(Semantics).isSmallestNormalized()); + + EXPECT_FALSE(APFloat::getLargest(Semantics).isSmallestNormalized()); + EXPECT_FALSE(APFloat::getLargest(Semantics, true).isSmallestNormalized()); + + EXPECT_FALSE(APFloat::getSmallest(Semantics).isSmallestNormalized()); + EXPECT_FALSE(APFloat::getSmallest(Semantics, true).isSmallestNormalized()); + + EXPECT_FALSE(APFloat::getAllOnesValue(Semantics).isSmallestNormalized()); + + APFloat PosSmallestNormalized = + APFloat::getSmallestNormalized(Semantics, false); + APFloat NegSmallestNormalized = + APFloat::getSmallestNormalized(Semantics, true); + EXPECT_TRUE(PosSmallestNormalized.isSmallestNormalized()); + EXPECT_TRUE(NegSmallestNormalized.isSmallestNormalized()); + + for (APFloat *Val : {&PosSmallestNormalized, &NegSmallestNormalized}) { + bool OldSign = Val->isNegative(); + + // Step down, make sure it's still not smallest normalized. + EXPECT_EQ(APFloat::opOK, Val->next(false)); + EXPECT_EQ(OldSign, Val->isNegative()); + EXPECT_FALSE(Val->isSmallestNormalized()); + EXPECT_EQ(OldSign, Val->isNegative()); + + // Step back up should restore it to being smallest normalized. + EXPECT_EQ(APFloat::opOK, Val->next(true)); + EXPECT_TRUE(Val->isSmallestNormalized()); + EXPECT_EQ(OldSign, Val->isNegative()); + + // Step beyond should no longer smallest normalized. + EXPECT_EQ(APFloat::opOK, Val->next(true)); + EXPECT_FALSE(Val->isSmallestNormalized()); + EXPECT_EQ(OldSign, Val->isNegative()); + } + } +} + TEST(APFloatTest, Zero) { EXPECT_EQ(0.0f, APFloat(0.0f).convertToFloat()); EXPECT_EQ(-0.0f, APFloat(-0.0f).convertToFloat()); Index: llvm/unittests/IR/PatternMatch.cpp =================================================================== --- llvm/unittests/IR/PatternMatch.cpp +++ llvm/unittests/IR/PatternMatch.cpp @@ -1519,6 +1519,19 @@ EXPECT_TRUE(match(CF32Pi, cstfp_pred_ty>())); EXPECT_FALSE(match(CF32Pi, cstfp_pred_ty>())); + APFloat PosSmallestNormal = + APFloat::getSmallestNormalized(APFloat::IEEEsingle()); + APFloat NegSmallestNormal = + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true); + Constant *CPosSmallestNormal = ConstantFP::get(F32Ty, PosSmallestNormal); + Constant *CNegSmallestNormal = ConstantFP::get(F32Ty, NegSmallestNormal); + + EXPECT_TRUE(match(CPosSmallestNormal, m_SmallestNormalized())); + EXPECT_TRUE(match(CNegSmallestNormal, m_SmallestNormalized())); + EXPECT_FALSE(match(CF32NaN, m_SmallestNormalized())); + EXPECT_FALSE(match(CF32Zero, m_SmallestNormalized())); + EXPECT_FALSE(match(CF32Pi, m_SmallestNormalized())); + auto FixedEC = ElementCount::getFixed(4); auto ScalableEC = ElementCount::getScalable(4);