Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -272,11 +272,16 @@ return isKnownNever(fcSubnormal); } - /// Return true if it's known this can never be a negativesubnormal + /// Return true if it's known this can never be a negative subnormal bool isKnownNeverNegSubnormal() const { return isKnownNever(fcNegSubnormal); } + /// Return true if it's known this can never be a positive subnormal + bool isKnownNeverPosSubnormal() const { + return isKnownNever(fcPosSubnormal); + } + /// Return true if it's known this can never be a zero. This means a literal /// [+-]0, and does not include denormal inputs implicitly treated as [+-]0. bool isKnownNeverZero() const { @@ -288,6 +293,11 @@ return isKnownNever(fcNegZero); } + /// Return true if it's known this can never be a literal positive zero. + bool isKnownNeverPosZero() const { + return isKnownNever(fcPosZero); + } + /// Return true if it's know this can never be interpreted as a zero. This /// extends isKnownNeverZero to cover the case where the assumed /// floating-point mode for the function interprets denormals as zero. @@ -296,6 +306,9 @@ /// Return true if it's know this can never be interpreted as a negative zero. bool isKnownNeverLogicalNegZero(const Function &F, Type *Ty) const; + /// Return true if it's know this can never be interpreted as a positive zero. + bool isKnownNeverLogicalPosZero(const Function &F, Type *Ty) const; + /// Return true if we can prove that the analyzed floating-point value is /// either NaN or never less than -0.0. /// Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -3912,6 +3912,20 @@ return F.getDenormalMode(Ty->getFltSemantics()).Input == DenormalMode::IEEE; } +static bool inputDenormalIsIEEEOrPosZero(const Function &F, const Type *Ty) { + Ty = Ty->getScalarType(); + DenormalMode Mode = F.getDenormalMode(Ty->getFltSemantics()); + return Mode.Output == DenormalMode::IEEE || + Mode.Output == DenormalMode::PositiveZero; +} + +static bool outputDenormalIsIEEEOrPosZero(const Function &F, const Type *Ty) { + Ty = Ty->getScalarType(); + DenormalMode Mode = F.getDenormalMode(Ty->getFltSemantics()); + return Mode.Output == DenormalMode::IEEE || + Mode.Output == DenormalMode::PositiveZero; +} + bool KnownFPClass::isKnownNeverLogicalZero(const Function &F, Type *Ty) const { return isKnownNeverZero() && (isKnownNeverSubnormal() || inputDenormalIsIEEE(F, Ty)); @@ -3922,6 +3936,13 @@ (isKnownNeverNegSubnormal() || inputDenormalIsIEEE(F, Ty)); } +bool KnownFPClass::isKnownNeverLogicalPosZero(const Function &F, + Type *Ty) const { + // TODO: Could refine subnormal handling for positive vs. preserve sign mode + return isKnownNeverPosZero() && + (isKnownNeverSubnormal() || inputDenormalIsIEEEOrPosZero(F, Ty)); +} + /// Returns a pair of values, which if passed to llvm.is.fpclass, returns the /// same result as an fcmp with the given operands. std::pair llvm::fcmpToClassTest(FCmpInst::Predicate Pred, @@ -4466,6 +4487,25 @@ KnownFPClass KnownLHS, KnownRHS; computeKnownFPClass(Op->getOperand(1), DemandedElts, fcNan | fcInf, KnownRHS, Depth + 1, Q, TLI); + + const Function *F = cast(Op)->getFunction(); + + if (Op->getOpcode() == Instruction::FAdd) { + // (fadd x, 0.0) is guaranteed to return +0.0, not -0.0. + if ((KnownLHS.isKnownNeverLogicalNegZero(*F, Op->getType()) || + KnownRHS.isKnownNeverLogicalNegZero(*F, Op->getType())) && + // Make sure output negative denormal can't flush to -0 + outputDenormalIsIEEEOrPosZero(*F, Op->getType())) + Known.knownNot(fcNegZero); + } else { + // Only fsub -0, +0 can return -0 + if ((KnownLHS.isKnownNeverLogicalNegZero(*F, Op->getType()) || + KnownRHS.isKnownNeverLogicalPosZero(*F, Op->getType())) && + // Make sure output negative denormal can't flush to -0 + outputDenormalIsIEEEOrPosZero(*F, Op->getType())) + Known.knownNot(fcNegZero); + } + if (KnownRHS.isKnownNeverNaN()) { // RHS is canonically cheaper to compute. Skip inspecting the LHS if // there's no point. Index: llvm/test/Transforms/Attributor/nofpclass.ll =================================================================== --- llvm/test/Transforms/Attributor/nofpclass.ll +++ llvm/test/Transforms/Attributor/nofpclass.ll @@ -1210,7 +1210,7 @@ define float @fadd_p0(float %arg0) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_p0 +; CHECK-LABEL: define nofpclass(nzero) float @fadd_p0 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG0]], 0.000000e+00 ; CHECK-NEXT: ret float [[ADD]] @@ -1243,7 +1243,7 @@ define float @fsub_n0(float %arg0) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fsub_n0 +; CHECK-LABEL: define nofpclass(nzero) float @fsub_n0 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[SUB:%.*]] = fsub float [[ARG0]], -0.000000e+00 ; CHECK-NEXT: ret float [[SUB]] @@ -1276,7 +1276,7 @@ define float @fadd_p0_ftz_daz(float %arg0) #3 { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_p0_ftz_daz +; CHECK-LABEL: define nofpclass(nzero) float @fadd_p0_ftz_daz ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR6:[0-9]+]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG0]], 0.000000e+00 ; CHECK-NEXT: ret float [[ADD]] @@ -1342,7 +1342,7 @@ define float @fadd_p0_ieee_daz(float %arg0) #2 { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_p0_ieee_daz +; CHECK-LABEL: define nofpclass(nzero) float @fadd_p0_ieee_daz ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR8:[0-9]+]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG0]], 0.000000e+00 ; CHECK-NEXT: ret float [[ADD]] @@ -1353,7 +1353,7 @@ define float @fadd_p0_dapz_ieee(float %arg0) #4 { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_p0_dapz_ieee +; CHECK-LABEL: define nofpclass(nzero) float @fadd_p0_dapz_ieee ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR9:[0-9]+]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG0]], 0.000000e+00 ; CHECK-NEXT: ret float [[ADD]] @@ -1386,7 +1386,7 @@ define float @fsub_n0_ieee_daz(float %arg0) #2 { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fsub_n0_ieee_daz +; CHECK-LABEL: define nofpclass(nzero) float @fsub_n0_ieee_daz ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR8]] { ; CHECK-NEXT: [[SUB:%.*]] = fsub float [[ARG0]], -0.000000e+00 ; CHECK-NEXT: ret float [[SUB]] @@ -1419,7 +1419,7 @@ define float @fadd_never_negzero_or_negsub(float nofpclass(nzero nsub) %a, float nofpclass(nzero nsub) %b) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_never_negzero_or_negsub +; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_negsub ; CHECK-SAME: (float nofpclass(nzero nsub) [[A:%.*]], float nofpclass(nzero nsub) [[B:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[A]], [[B]] ; CHECK-NEXT: ret float [[ADD]] @@ -1430,7 +1430,7 @@ define float @fadd_never_negzero_or_negsub_daz(float nofpclass(nzero nsub) %a, float nofpclass(nzero nsub) %b) #2 { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_never_negzero_or_negsub_daz +; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_negsub_daz ; CHECK-SAME: (float nofpclass(nzero nsub) [[A:%.*]], float nofpclass(nzero nsub) [[B:%.*]]) #[[ATTR8]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[A]], [[B]] ; CHECK-NEXT: ret float [[ADD]] @@ -1441,7 +1441,7 @@ define float @fadd_never_negzero_or_negsub_dapz(float nofpclass(nzero nsub) %a, float nofpclass(nzero nsub) %b) #5 { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_never_negzero_or_negsub_dapz +; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_negsub_dapz ; CHECK-SAME: (float nofpclass(nzero nsub) [[A:%.*]], float nofpclass(nzero nsub) [[B:%.*]]) #[[ATTR11:[0-9]+]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[A]], [[B]] ; CHECK-NEXT: ret float [[ADD]] @@ -1452,7 +1452,7 @@ define float @fadd_never_negzero_or_possub(float nofpclass(nzero psub) %a, float nofpclass(nzero psub) %b) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_never_negzero_or_possub +; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_possub ; CHECK-SAME: (float nofpclass(nzero psub) [[A:%.*]], float nofpclass(nzero psub) [[B:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[A]], [[B]] ; CHECK-NEXT: ret float [[ADD]] @@ -1485,7 +1485,7 @@ define float @fadd_never_negzero_or_sub_daz(float nofpclass(nzero sub) %a, float nofpclass(nzero sub) %b) #2 { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_never_negzero_or_sub_daz +; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_sub_daz ; CHECK-SAME: (float nofpclass(nzero sub) [[A:%.*]], float nofpclass(nzero sub) [[B:%.*]]) #[[ATTR8]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[A]], [[B]] ; CHECK-NEXT: ret float [[ADD]] @@ -1496,7 +1496,7 @@ define float @fadd_never_negzero_or_sub_dapz(float nofpclass(nzero sub) %a, float nofpclass(nzero sub) %b) #5 { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_never_negzero_or_sub_dapz +; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_sub_dapz ; CHECK-SAME: (float nofpclass(nzero sub) [[A:%.*]], float nofpclass(nzero sub) [[B:%.*]]) #[[ATTR11]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[A]], [[B]] ; CHECK-NEXT: ret float [[ADD]] @@ -1562,7 +1562,7 @@ define float @fadd_known_positive_nzero_rhs(float nofpclass(ninf nsub nnorm) %arg0, float nofpclass(ninf nsub nnorm nzero) %arg1) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_known_positive_nzero_rhs +; CHECK-LABEL: define nofpclass(nzero) float @fadd_known_positive_nzero_rhs ; CHECK-SAME: (float nofpclass(ninf nsub nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]] ; CHECK-NEXT: ret float [[ADD]] @@ -1573,7 +1573,7 @@ define float @fadd_known_positive_nzero(float nofpclass(ninf nsub nnorm nzero) %arg0, float nofpclass(ninf nsub nnorm nzero) %arg1) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_known_positive_nzero +; CHECK-LABEL: define nofpclass(nzero) float @fadd_known_positive_nzero ; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]] ; CHECK-NEXT: ret float [[ADD]] @@ -1606,7 +1606,7 @@ define float @fadd_known_positive_nzero_daz(float nofpclass(ninf nsub nnorm nzero) %arg0, float nofpclass(ninf nsub nnorm nzero) %arg1) #2 { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_known_positive_nzero_daz +; CHECK-LABEL: define nofpclass(nzero) float @fadd_known_positive_nzero_daz ; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR8]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]] ; CHECK-NEXT: ret float [[ADD]] @@ -1617,7 +1617,7 @@ define float @fadd_known_positive_normal(float nofpclass(ninf nnorm nzero) %arg0, float nofpclass(ninf nnorm nzero) %arg1) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @fadd_known_positive_normal +; CHECK-LABEL: define nofpclass(nzero) float @fadd_known_positive_normal ; CHECK-SAME: (float nofpclass(ninf nzero nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nnorm) [[ARG1:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]] ; CHECK-NEXT: ret float [[ADD]]