Index: llvm/include/llvm/IR/IRBuilder.h =================================================================== --- llvm/include/llvm/IR/IRBuilder.h +++ llvm/include/llvm/IR/IRBuilder.h @@ -2425,6 +2425,11 @@ Name); } + /// Return a boolean value testing if \p Arg is a NaN. + Value *CreateIsNaN(Value *Arg, const Twine &Name = "") { + return CreateFCmpUNO(Arg, ConstantFP::getZero(Arg->getType()), Name); + } + /// Return the i64 difference between two pointer values, dividing out /// the size of the pointed-to objects. /// Index: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -821,6 +821,44 @@ return nullptr; } +Instruction *InstCombinerImpl::foldIntrinsicIsFPClass(IntrinsicInst &II) { + Value *Src0 = II.getArgOperand(0); + Value *Src1 = II.getArgOperand(1); + const ConstantInt *CMask = cast(Src1); + uint32_t Mask = CMask->getZExtValue(); + const bool IsStrict = II.isStrictFP(); + + if (Mask == fcNan && !IsStrict) { + // Equivalent of isnan. Replace with standard fcmp if we don't care about FP + // exceptions. + Value *FCmp = Builder.CreateIsNaN(Src0); + FCmp->takeName(&II); + return replaceInstUsesWith(II, FCmp); + } + + if (Mask == (~fcNan & fcAllFlags) && !IsStrict) { + // Equivalent of !isnan. Replace with standard fcmp. + Value *FCmp = + Builder.CreateFCmpORD(Src0, ConstantFP::getZero(Src0->getType())); + FCmp->takeName(&II); + return replaceInstUsesWith(II, FCmp); + } + + // fp_class (nnan x), qnan|snan|other -> fp_class (nnan x), other + if ((Mask & fcNan) && isKnownNeverNaN(Src0, &getTargetLibraryInfo())) { + return replaceOperand(II, 1, + ConstantInt::get(Src1->getType(), Mask & ~fcNan)); + } + + // fp_class (nnan x), ~(qnan|snan) -> true + if (Mask == (~fcNan & fcAllFlags) && + isKnownNeverNaN(Src0, &getTargetLibraryInfo())) { + return replaceInstUsesWith(II, ConstantInt::get(II.getType(), true)); + } + + return nullptr; +} + static Optional getKnownSign(Value *Op, Instruction *CxtI, const DataLayout &DL, AssumptionCache *AC, DominatorTree *DT) { @@ -2830,6 +2868,11 @@ } break; } + case Intrinsic::is_fpclass: { + if (Instruction *I = foldIntrinsicIsFPClass(*II)) + return I; + break; + } default: { // Handle target specific intrinsics std::optional V = targetInstCombineIntrinsic(*II); Index: llvm/lib/Transforms/InstCombine/InstCombineInternal.h =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -369,6 +369,7 @@ Instruction *foldExtractOfOverflowIntrinsic(ExtractValueInst &EV); Instruction *foldIntrinsicWithOverflowCommon(IntrinsicInst *II); + Instruction *foldIntrinsicIsFPClass(IntrinsicInst &II); Instruction *foldFPSignBitOps(BinaryOperator &I); Instruction *foldFDivConstantDivisor(BinaryOperator &I); Index: llvm/test/Transforms/InstCombine/is_fpclass.ll =================================================================== --- llvm/test/Transforms/InstCombine/is_fpclass.ll +++ llvm/test/Transforms/InstCombine/is_fpclass.ll @@ -89,13 +89,22 @@ define i1 @test_class_isnan_f32(float %x) { ; CHECK-LABEL: @test_class_isnan_f32( -; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 3) +; CHECK-NEXT: [[VAL:%.*]] = fcmp uno float [[X:%.*]], 0.000000e+00 ; CHECK-NEXT: ret i1 [[VAL]] ; %val = call i1 @llvm.is.fpclass.f32(float %x, i32 3) ret i1 %val } +define <2 x i1> @test_class_isnan_v2f32(<2 x float> %x) { +; CHECK-LABEL: @test_class_isnan_v2f32( +; CHECK-NEXT: [[VAL:%.*]] = fcmp uno <2 x float> [[X:%.*]], zeroinitializer +; CHECK-NEXT: ret <2 x i1> [[VAL]] +; + %val = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> %x, i32 3) + ret <2 x i1> %val +} + define i1 @test_class_isnan_f32_strict(float %x) { ; CHECK-LABEL: @test_class_isnan_f32_strict( ; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 3) #[[ATTR3:[0-9]+]] @@ -399,9 +408,7 @@ define i1 @test_class_is_snan_nnan_src(float %x) { ; 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]] +; CHECK-NEXT: ret i1 false ; %nnan = fadd nnan float %x, 1.0 %class = call i1 @llvm.is.fpclass.f32(float %nnan, i32 1) @@ -410,9 +417,7 @@ define i1 @test_class_is_qnan_nnan_src(float %x) { ; 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]] +; CHECK-NEXT: ret i1 false ; %nnan = fadd nnan float %x, 1.0 %class = call i1 @llvm.is.fpclass.f32(float %nnan, i32 2) @@ -421,9 +426,7 @@ define i1 @test_class_is_nan_nnan_src(float %x) { ; 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]] +; CHECK-NEXT: ret i1 false ; %nnan = fadd nnan float %x, 1.0 %class = call i1 @llvm.is.fpclass.f32(float %nnan, i32 3) @@ -433,7 +436,7 @@ define i1 @test_class_is_nan_other_nnan_src(float %x) { ; 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: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NNAN]], i32 264) ; CHECK-NEXT: ret i1 [[CLASS]] ; %nnan = fadd nnan float %x, 1.0 @@ -444,9 +447,7 @@ ; Fold test of is not nan define i1 @test_class_is_not_nan_nnan_src(float %x) { ; CHECK-LABEL: @test_class_is_not_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 1020) -; CHECK-NEXT: ret i1 [[CLASS]] +; CHECK-NEXT: ret i1 true ; %nnan = fadd nnan float %x, 1.0 %class = call i1 @llvm.is.fpclass.f32(float %nnan, i32 1020) ; ~fcNan & fcAllFlags @@ -455,9 +456,7 @@ define i1 @test_class_is_not_nan_nnan_src_strict(float %x) { ; CHECK-LABEL: @test_class_is_not_nan_nnan_src_strict( -; CHECK-NEXT: [[NNAN:%.*]] = fadd nnan float [[X:%.*]], 1.000000e+00 -; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NNAN]], i32 1020) #[[ATTR3]] -; CHECK-NEXT: ret i1 [[CLASS]] +; CHECK-NEXT: ret i1 true ; %nnan = fadd nnan float %x, 1.0 %class = call i1 @llvm.is.fpclass.f32(float %nnan, i32 1020) strictfp ; ~fcNan & fcAllFlags @@ -540,9 +539,8 @@ define i1 @test_class_not_is_nan(float %x) { ; CHECK-LABEL: @test_class_not_is_nan( -; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 3) -; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[CLASS]], true -; CHECK-NEXT: ret i1 [[NOT]] +; CHECK-NEXT: [[CLASS:%.*]] = fcmp ord float [[X:%.*]], 0.000000e+00 +; CHECK-NEXT: ret i1 [[CLASS]] ; %class = call i1 @llvm.is.fpclass.f32(float %x, i32 3) %not = xor i1 %class, true @@ -551,7 +549,7 @@ define i1 @test_class_not_is_nan_multi_use(float %x, ptr %ptr) { ; CHECK-LABEL: @test_class_not_is_nan_multi_use( -; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 3) +; CHECK-NEXT: [[CLASS:%.*]] = fcmp uno float [[X:%.*]], 0.000000e+00 ; CHECK-NEXT: store i1 [[CLASS]], ptr [[PTR:%.*]], align 1 ; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[CLASS]], true ; CHECK-NEXT: ret i1 [[NOT]]