Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -4363,6 +4363,11 @@ KnownFPClass KnownLHS, KnownRHS; computeKnownFPClass(Op->getOperand(1), DemandedElts, fcNan | fcInf, KnownRHS, Depth + 1, Q, TLI); + + // (fadd|fsub x, 0.0) is guaranteed to return +0.0, not -0.0. + if (KnownRHS.KnownFPClasses == fcPosZero) + 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 @@ -1095,3 +1095,69 @@ %cvt = sitofp i32 %arg to float ret float %cvt } + +define float @fadd_p0(float %arg0) { +; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; 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]] +; + %add = fadd float %arg0, 0.0 + ret float %add +} + +define float @fadd_n0(float %arg0) { +; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CHECK-LABEL: define float @fadd_n0 +; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG0]], -0.000000e+00 +; CHECK-NEXT: ret float [[ADD]] +; + %add = fadd float %arg0, -0.0 + ret float %add +} + +define float @fsub_p0(float %arg0) { +; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CHECK-LABEL: define nofpclass(nzero) float @fsub_p0 +; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[SUB:%.*]] = fsub float [[ARG0]], 0.000000e+00 +; CHECK-NEXT: ret float [[SUB]] +; + %sub = fsub float %arg0, 0.0 + ret float %sub +} + +define float @fsub_n0(float %arg0) { +; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CHECK-LABEL: define float @fsub_n0 +; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[SUB:%.*]] = fsub float [[ARG0]], -0.000000e+00 +; CHECK-NEXT: ret float [[SUB]] +; + %sub = fsub float %arg0, -0.0 + ret float %sub +} + +define float @fsub_p0_commute(float %arg0) { +; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CHECK-LABEL: define float @fsub_p0_commute +; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[SUB:%.*]] = fsub float 0.000000e+00, [[ARG0]] +; CHECK-NEXT: ret float [[SUB]] +; + %sub = fsub float 0.0, %arg0 + ret float %sub +} + +define float @fsub_n0_commute(float %arg0) { +; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CHECK-LABEL: define float @fsub_n0_commute +; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[SUB:%.*]] = fsub float -0.000000e+00, [[ARG0]] +; CHECK-NEXT: ret float [[SUB]] +; + %sub = fsub float -0.0, %arg0 + ret float %sub +}