Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -4148,26 +4148,31 @@ return; } - const Operator *Op = dyn_cast(V); - if (!Op) - return; - FPClassTest KnownNotFromFlags = fcNone; - if (const FPMathOperator *FPOp = dyn_cast(Op)) { + if (const auto *CB = dyn_cast(V)) + KnownNotFromFlags |= CB->getRetNoFPClass(); + else if (const auto *Arg = dyn_cast(V)) + KnownNotFromFlags |= Arg->getNoFPClass(); + + const Operator *Op = dyn_cast(V); + if (const FPMathOperator *FPOp = dyn_cast_or_null(Op)) { if (FPOp->hasNoNaNs()) KnownNotFromFlags |= fcNan; if (FPOp->hasNoInfs()) KnownNotFromFlags |= fcInf; - - // We no longer need to find out about these bits from inputs if we can - // assume this from flags. - InterestedClasses &= ~KnownNotFromFlags; } + // We no longer need to find out about these bits from inputs if we can + // assume this from flags/attributes. + InterestedClasses &= ~KnownNotFromFlags; + auto ClearClassesFromFlags = make_scope_exit([=, &Known] { Known.knownNot(KnownNotFromFlags); }); + if (!Op) + return; + // All recursive calls that increase depth must come after this. if (Depth == MaxAnalysisRecursionDepth) return; Index: llvm/unittests/Analysis/ValueTrackingTest.cpp =================================================================== --- llvm/unittests/Analysis/ValueTrackingTest.cpp +++ llvm/unittests/Analysis/ValueTrackingTest.cpp @@ -1354,6 +1354,83 @@ expectKnownFPClass(~(fcNan | fcInf), std::nullopt); } +TEST_F(ComputeKnownFPClassTest, SelectNoFPClassArgUnionAll) { + parseAssembly( + "define float @test(i1 %cond, float nofpclass(snan ninf nsub pzero pnorm) %arg0, float nofpclass(qnan nnorm nzero psub pinf) %arg1) {\n" + " %A = select i1 %cond, float %arg0, float %arg1" + " ret float %A\n" + "}\n"); + expectKnownFPClass(fcAllFlags, std::nullopt); +} + +TEST_F(ComputeKnownFPClassTest, SelectNoFPClassArgNoNan) { + parseAssembly( + "define float @test(i1 %cond, float nofpclass(nan) %arg0, float nofpclass(nan) %arg1) {\n" + " %A = select i1 %cond, float %arg0, float %arg1" + " ret float %A\n" + "}\n"); + expectKnownFPClass(~fcNan, std::nullopt); +} + +TEST_F(ComputeKnownFPClassTest, SelectNoFPClassArgNoPInf) { + parseAssembly( + "define float @test(i1 %cond, float nofpclass(inf) %arg0, float nofpclass(pinf) %arg1) {\n" + " %A = select i1 %cond, float %arg0, float %arg1" + " ret float %A\n" + "}\n"); + expectKnownFPClass(~fcPosInf, std::nullopt); +} + +TEST_F(ComputeKnownFPClassTest, SelectNoFPClassArgNoNInf) { + parseAssembly( + "define float @test(i1 %cond, float nofpclass(ninf) %arg0, float nofpclass(inf) %arg1) {\n" + " %A = select i1 %cond, float %arg0, float %arg1" + " ret float %A\n" + "}\n"); + expectKnownFPClass(~fcNegInf, std::nullopt); +} + +TEST_F(ComputeKnownFPClassTest, SelectNoFPClassCallSiteNoNan) { + parseAssembly( + "declare float @func()\n" + "define float @test() {\n" + " %A = call nofpclass(nan) float @func()\n" + " ret float %A\n" + "}\n"); + expectKnownFPClass(~fcNan, std::nullopt); +} + +TEST_F(ComputeKnownFPClassTest, SelectNoFPClassCallSiteNoZeros) { + parseAssembly( + "declare float @func()\n" + "define float @test() {\n" + " %A = call nofpclass(zero) float @func()\n" + " ret float %A\n" + "}\n"); + expectKnownFPClass(~fcZero, std::nullopt); +} + +TEST_F(ComputeKnownFPClassTest, SelectNoFPClassDeclarationNoNan) { + parseAssembly( + "declare nofpclass(nan) float @no_nans()\n" + "define float @test() {\n" + " %A = call float @no_nans()\n" + " ret float %A\n" + "}\n"); + expectKnownFPClass(~fcNan, std::nullopt); +} + +// Check nofpclass + ninf works on a callsite +TEST_F(ComputeKnownFPClassTest, SelectNoFPClassCallSiteNoZerosNInfFlags) { + parseAssembly( + "declare float @func()\n" + "define float @test() {\n" + " %A = call ninf nofpclass(zero) float @func()\n" + " ret float %A\n" + "}\n"); + expectKnownFPClass(~(fcZero | fcInf), std::nullopt); +} + TEST_F(ComputeKnownFPClassTest, FNegNInf) { parseAssembly( "define float @test(float %arg) {\n"