Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -347,8 +347,13 @@ SignBit = false; } + /// Return true if the sign bit must be 0, ignoring the sign of nans. + bool signBitIsZeroOrNaN() const { + return isKnownNever(fcNegative); + } + /// Assume the sign bit is zero. - void signBitIsZero() { + void signBitMustBeZero() { KnownFPClasses = (KnownFPClasses & fcPositive) | (KnownFPClasses & fcNan); SignBit = false; Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -4444,7 +4444,7 @@ Known.knownNot(fcSubnormal); if (IID == Intrinsic::experimental_constrained_uitofp) - Known.signBitIsZero(); + Known.signBitMustBeZero(); // TODO: Copy inf handling from instructions break; @@ -4537,8 +4537,17 @@ break; } case Instruction::FDiv: { + // X / X is always exactly 1.0 or a NaN. + if (Op->getOperand(0) == Op->getOperand(1)) { + // TODO: Could filter out snan if we inspect the operand + Known.KnownFPClasses = fcNan | fcPosNormal; + break; + } + const bool WantInfNan = (InterestedClasses & (fcInf | fcNan)) != fcNone; - if (!WantInfNan) + const bool WantNegative = + (InterestedClasses & KnownFPClass::OrderedLessThanZeroMask) != fcNone; + if (!WantInfNan && !WantNegative) break; // TODO: FRem @@ -4548,13 +4557,15 @@ fcNan | fcInf | fcZero | fcSubnormal, KnownRHS, Depth + 1, Q, TLI); - bool KnowSomethingUseful = KnownRHS.isKnownNeverNaN() || - KnownRHS.isKnownNeverInfinity() || - KnownRHS.isKnownNeverZero(); + bool KnowSomethingUseful = + KnownRHS.isKnownNeverNaN() || KnownRHS.isKnownNeverInfinity() || + KnownRHS.isKnownNeverZero() || KnownRHS.signBitIsZeroOrNaN(); - if (KnowSomethingUseful && WantInfNan) { + if (KnowSomethingUseful && (WantInfNan || WantNegative)) { computeKnownFPClass(Op->getOperand(0), DemandedElts, - fcNan | fcInf | fcZero, KnownLHS, Depth + 1, Q, TLI); + fcNan | fcInf | fcZero | + KnownFPClass::OrderedLessThanZeroMask, + KnownLHS, Depth + 1, Q, TLI); } const Function *F = cast(Op)->getFunction(); @@ -4568,6 +4579,9 @@ Known.knownNot(fcNan); } + // X / -0.0 is -Inf (or NaN). + if (KnownLHS.cannotBeOrderedLessThanZero() && KnownRHS.signBitIsZeroOrNaN()) + Known.knownNot(KnownFPClass::OrderedLessThanZeroMask); break; } case Instruction::FPExt: { @@ -4605,7 +4619,7 @@ // sitofp and uitofp turn into +0.0 for zero. Known.knownNot(fcNegZero); if (Op->getOpcode() == Instruction::UIToFP) - Known.signBitIsZero(); + Known.signBitMustBeZero(); if (InterestedClasses & fcInf) { // Get width of largest magnitude integer (remove a bit if signed). Index: llvm/test/Transforms/Attributor/nofpclass-fdiv.ll =================================================================== --- llvm/test/Transforms/Attributor/nofpclass-fdiv.ll +++ llvm/test/Transforms/Attributor/nofpclass-fdiv.ll @@ -356,7 +356,7 @@ } define float @ret_fdiv_same_operands(float %arg) #0 { -; CHECK-LABEL: define float @ret_fdiv_same_operands +; CHECK-LABEL: define nofpclass(inf zero sub nnorm) float @ret_fdiv_same_operands ; CHECK-SAME: (float [[ARG:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]] ; CHECK-NEXT: ret float [[FDIV]] @@ -366,7 +366,7 @@ } define float @ret_fdiv_same_operands_nosnan(float nofpclass(snan) %arg) #0 { -; CHECK-LABEL: define float @ret_fdiv_same_operands_nosnan +; CHECK-LABEL: define nofpclass(inf zero sub nnorm) float @ret_fdiv_same_operands_nosnan ; CHECK-SAME: (float nofpclass(snan) [[ARG:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]] ; CHECK-NEXT: ret float [[FDIV]] @@ -376,7 +376,7 @@ } define float @ret_fdiv_same_operands_noqnan(float nofpclass(qnan) %arg) #0 { -; CHECK-LABEL: define float @ret_fdiv_same_operands_noqnan +; CHECK-LABEL: define nofpclass(inf zero sub nnorm) float @ret_fdiv_same_operands_noqnan ; CHECK-SAME: (float nofpclass(qnan) [[ARG:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]] ; CHECK-NEXT: ret float [[FDIV]] @@ -386,7 +386,7 @@ } define float @ret_fdiv_same_operands_nonan(float nofpclass(nan) %arg) #0 { -; CHECK-LABEL: define float @ret_fdiv_same_operands_nonan +; CHECK-LABEL: define nofpclass(inf zero sub nnorm) float @ret_fdiv_same_operands_nonan ; CHECK-SAME: (float nofpclass(nan) [[ARG:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]] ; CHECK-NEXT: ret float [[FDIV]] @@ -436,7 +436,7 @@ } define float @ret_fdiv_no_neg_nzero(float nofpclass(ninf nsub nnorm nzero) %arg0, float nofpclass(ninf nsub nnorm nzero) %arg1) #0 { -; CHECK-LABEL: define float @ret_fdiv_no_neg_nzero +; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_fdiv_no_neg_nzero ; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]] ; CHECK-NEXT: ret float [[FDIV]] @@ -446,7 +446,7 @@ } define float @ret_fdiv_no_neg_rhs_no_nzero(float nofpclass(ninf nsub nnorm) %arg0, float nofpclass(ninf nsub nnorm nzero) %arg1) #0 { -; CHECK-LABEL: define float @ret_fdiv_no_neg_rhs_no_nzero +; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_fdiv_no_neg_rhs_no_nzero ; CHECK-SAME: (float nofpclass(ninf nsub nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]] ; CHECK-NEXT: ret float [[FDIV]] @@ -456,7 +456,7 @@ } define float @ret_fdiv_no_neg_no_zero_rhs(float nofpclass(ninf nsub nnorm nzero) %arg0, float nofpclass(ninf nsub nnorm zero) %arg1) #0 { -; CHECK-LABEL: define float @ret_fdiv_no_neg_no_zero_rhs +; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_fdiv_no_neg_no_zero_rhs ; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]], float nofpclass(ninf zero nsub nnorm) [[ARG1:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]] ; CHECK-NEXT: ret float [[FDIV]]