ValueTracking: Generalize known frexp exponent range to any constant (#208316)

Extend computeKnownExponentRangeFromContext beyond the special case of a
dominating fabs(V) compare against exactly 1.0 to any finite limit.

Generalizing below 1.0 exposes a soundness issue: frexp(0) has exponent
0, so a bound derived assuming a nonzero value could wrongly exclude it
when the limit implies a negative maximum exponent. Query fcZero and
clamp the maximum exponent to at least 0 when the source may be zero.

Co-authored-by: Claude (Opus 4.8) <noreply@anthropic.com>
This commit is contained in:
Matt Arsenault 2026-07-10 20:24:53 +02:00 committed by GitHub
parent 2396c15bd3
commit 188b9a3bfb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 519 additions and 57 deletions

View file

@ -4897,11 +4897,20 @@ static void computeKnownFPClassFromCond(const Value *V, Value *Cond,
}
/// Compute the minimum and maximum values (inclusive) for the exponent of \p V,
/// assuming it is not nan.
static std::pair<int, int>
/// assuming it is not nan. Returns {min, max, max-assuming-nonzero}. A value
/// frexp(0) = 0, so the tighter max-assuming-nonzero bound is only usable when
/// \p V is known not to be a logical zero (e.g., for fabs(x) < 0.25, the non-0
/// exponent range is [-149, -2], but the 0 edge case is above this range).
static std::tuple<int, int, int>
computeKnownExponentRangeFromContext(const Value *V, const SimplifyQuery &Q) {
if (!Q.CxtI || !Q.DC || !Q.DT)
return {APFloat::IEK_NaN, APFloat::IEK_Inf};
return {APFloat::IEK_NaN, APFloat::IEK_Inf, APFloat::IEK_Inf};
// Intersect the bounds implied by every dominating condition, keeping the
// tightest maximum. A value may participate in multiple compares
// (e.g. fabs(x) < 2.0 and fabs(x) < 1.0), and the tighter one wins.
int MaxExp = APFloat::IEK_Inf;
int MaxExpNonZero = APFloat::IEK_Inf;
for (CondBrInst *BI : Q.DC->conditionsFor(V)) {
CmpPredicate Pred;
@ -4914,13 +4923,8 @@ computeKnownExponentRangeFromContext(const Value *V, const SimplifyQuery &Q) {
Pred == FCmpInst::FCMP_TRUE || Pred == FCmpInst::FCMP_FALSE)
continue;
// TOOD: Handle general constant values.
if (LimitC->compare(APFloat::getOne(LimitC->getSemantics())) !=
APFloat::cmpEqual)
continue;
// If fabs(x) <= K, K <= 1.0 => exponent min exp range
// if fabs(x) >= K, K <= 1.0 swap the successor
// If fabs(x) <= K, implies the exponent min exp range.
// if fabs(x) >= K, swap the successor
bool IsLessEqual =
Pred == FCmpInst::FCMP_OLT || Pred == FCmpInst::FCMP_OLE ||
Pred == FCmpInst::FCMP_ULT || Pred == FCmpInst::FCMP_ULE ||
@ -4933,17 +4937,24 @@ computeKnownExponentRangeFromContext(const Value *V, const SimplifyQuery &Q) {
BasicBlockEdge Edge1(BI->getParent(),
BI->getSuccessor(IsLessEqual ? 0 : 1));
if (Q.DT->dominates(Edge1, Q.CxtI->getParent())) {
// frexp returns an exponent one greater than ilogb. For a limit of 1.0
// this is 1; if the value is known strictly less than 1.0 the maximum
// exponent is instead 0.
int Exp = KnownStrictlyLess ? 0 : 1;
// frexp returns an exponent one greater than ilogb.
int Exp = ilogb(*LimitC) + 1;
// A strict bound fabs(V) < 2^n forces ilogb(V) <= n - 1, so the max frexp
// exponent drops by one when K is exact power of two.
if (KnownStrictlyLess && LimitC->getExactLog2Abs() != INT_MIN)
--Exp;
// frexp(0) = 0, which the bound above (assuming a normal nonzero value)
// may exclude.
// TODO: Figure out lower bound to detect no-underflow.
return {APFloat::IEK_NaN, Exp};
MaxExpNonZero = std::min(MaxExpNonZero, Exp);
MaxExp = std::min(MaxExp, std::max(Exp, 0));
}
}
return {APFloat::IEK_NaN, APFloat::IEK_Inf};
return {APFloat::IEK_NaN, MaxExp, MaxExpNonZero};
}
static KnownFPClass computeKnownFPClassFromContext(const Value *V,
@ -10547,7 +10558,7 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,
// only computes the range assuming standard subnormal handling.
if (APFloat::isIEEELikeFP(FltSem)) {
KnownFPClass KnownSrc = computeKnownFPClass(
FrexpSrc, fcSubnormal | fcNan | fcInf, SQ, Depth + 1);
FrexpSrc, fcSubnormal | fcZero | fcNan | fcInf, SQ, Depth + 1);
// The exponent of frexp(NaN) and frexp(Inf) is unspecified. Only
// constrain its range when the source can be neither.
@ -10560,11 +10571,15 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,
int MaxExp = APFloat::semanticsMaxExponent(FltSem) + 1;
auto [AdjustedMin, AdjustedMax] =
auto [AdjustedMin, AdjustedMax, AdjustedMaxNonZero] =
computeKnownExponentRangeFromContext(FrexpSrc, SQ);
DenormalMode Mode = I->getFunction()->getDenormalMode(FltSem);
bool NeverLogicalZero = KnownSrc.isKnownNeverLogicalZero(Mode);
MinExp = std::max(AdjustedMin, MinExp);
MaxExp = std::min(AdjustedMax, MaxExp);
MaxExp = std::min(NeverLogicalZero ? AdjustedMaxNonZero : AdjustedMax,
MaxExp);
CR = ConstantRange::getNonEmpty(
APInt(BitWidth, static_cast<int64_t>(MinExp), /*isSigned=*/true),

View file

@ -266,9 +266,7 @@ define float @frexp_cmp_ugt_0.5(float nofpclass(nan inf) %x, float nofpclass(inf
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -298,9 +296,7 @@ define float @frexp_cmp_uge_0.5(float nofpclass(nan inf) %x, float nofpclass(inf
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -330,9 +326,7 @@ define float @frexp_fcmp_ult_0.5(float nofpclass(nan inf) %x, float nofpclass(in
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -362,9 +356,7 @@ define float @frexp_fcmp_ule_0.5(float nofpclass(nan inf) %x, float nofpclass(in
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -394,9 +386,7 @@ define float @frexp_fcmp_ugt_0.5(float nofpclass(nan inf) %x, float nofpclass(in
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -426,9 +416,7 @@ define float @frexp_fcmp_uge_0.5(float nofpclass(nan inf) %x, float nofpclass(in
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -586,9 +574,7 @@ define float @frexp_fcmp_oeq_0.5(float nofpclass(nan inf) %x, float nofpclass(in
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -618,9 +604,7 @@ define float @frexp_fcmp_ueq_0.5(float nofpclass(nan inf) %x, float nofpclass(in
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -650,9 +634,7 @@ define float @frexp_fcmp_one_0.5(float nofpclass(nan inf) %x, float nofpclass(in
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -682,9 +664,7 @@ define float @frexp_fcmp_une_0.5(float nofpclass(nan inf) %x, float nofpclass(in
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -891,9 +871,7 @@ define float @frexp_fcmp_ogt_nextdown1(float nofpclass(nan inf) %x, float nofpcl
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -924,9 +902,7 @@ define float @frexp_fcmp_ole_nextdown1(float nofpclass(nan inf) %x, float nofpcl
; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[EXP:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
; CHECK-NEXT: [[SCALED:%.*]] = call float @llvm.ldexp.f32.i32(float [[NOT_INF]], i32 [[EXP]])
; CHECK-NEXT: [[IS_INF:%.*]] = fcmp oeq float [[SCALED]], +inf
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[IS_INF]], float 0.000000e+00, float [[SCALED]]
; CHECK-NEXT: ret float [[SELECT]]
; CHECK-NEXT: ret float [[SCALED]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret float 0.000000e+00
;
@ -1979,8 +1955,7 @@ large:
ret i1 false
}
; For now only a limit of exactly 1.0 is handled. A limit below 1.0
; must not imply a negative maximum exponent.
; A limit below 1.0 must not imply a negative maximum exponent.
define i1 @frexp_exp_olt_below1_zero_input(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_below1_zero_input(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
@ -2010,3 +1985,475 @@ small:
large:
ret i1 false
}
; K = 0.25 = 2^-2, strict, source known nonzero: fabs(x) < 2^-2 forces the
; frexp exponent <= -2.
define i1 @frexp_exp_olt_quarter_nonzero_fold(float nofpclass(nan inf zero) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_quarter_nonzero_fold(
; CHECK-SAME: float nofpclass(nan inf zero) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 2.500000e-01
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: ret i1 true
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 2.500000e-01
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, -2
ret i1 %r
large:
ret i1 false
}
define i1 @frexp_exp_olt_quarter_nonzero_nofold(float nofpclass(nan inf zero) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_quarter_nonzero_nofold(
; CHECK-SAME: float nofpclass(nan inf zero) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 2.500000e-01
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: [[FX:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[E:%.*]] = extractvalue { float, i32 } [[FX]], 1
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[E]], -2
; CHECK-NEXT: ret i1 [[R]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 2.500000e-01
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, -3
ret i1 %r
large:
ret i1 false
}
; K = 0.25 = 2^-2, strict, source may be zero: frexp(+/-0) has exponent 0, so
; the maximum is clamped up to 0.
define i1 @frexp_exp_olt_quarter_maybezero_fold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_quarter_maybezero_fold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 2.500000e-01
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: ret i1 true
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 2.500000e-01
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 0
ret i1 %r
large:
ret i1 false
}
define i1 @frexp_exp_olt_quarter_maybezero_nofold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_quarter_maybezero_nofold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 2.500000e-01
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: [[FX:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[E:%.*]] = extractvalue { float, i32 } [[FX]], 1
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[E]], 0
; CHECK-NEXT: ret i1 [[R]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 2.500000e-01
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, -1
ret i1 %r
large:
ret i1 false
}
; K = 0.75, below 1.0 but not a power of two: no strict power-of-two
; adjustment, maximum exponent 0.
define i1 @frexp_exp_olt_0p75_fold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_0p75_fold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 7.500000e-01
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: ret i1 true
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 7.500000e-01
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 0
ret i1 %r
large:
ret i1 false
}
define i1 @frexp_exp_olt_0p75_nofold(float nofpclass(nan inf zero) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_0p75_nofold(
; CHECK-SAME: float nofpclass(nan inf zero) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 7.500000e-01
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: [[FX:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[E:%.*]] = extractvalue { float, i32 } [[FX]], 1
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[E]], 0
; CHECK-NEXT: ret i1 [[R]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 7.500000e-01
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, -1
ret i1 %r
large:
ret i1 false
}
; K = 4.0 = 2^2, strict: fabs(x) < 2^2 forces the frexp exponent <= 2.
define i1 @frexp_exp_olt_4_fold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_4_fold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 4.000000e+00
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: ret i1 true
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 4.000000e+00
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 2
ret i1 %r
large:
ret i1 false
}
define i1 @frexp_exp_olt_4_nofold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_4_nofold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 4.000000e+00
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: [[FX:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[E:%.*]] = extractvalue { float, i32 } [[FX]], 1
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[E]], 2
; CHECK-NEXT: ret i1 [[R]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 4.000000e+00
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 1
ret i1 %r
large:
ret i1 false
}
; K = 4.0 = 2^2, non-strict: fabs(x) <= 2^2 permits the frexp exponent 3 (from
; x = 4.0), one more than the strict case.
define i1 @frexp_exp_ole_4_fold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_ole_4_fold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp ugt float [[ABS]], 4.000000e+00
; CHECK-NEXT: br i1 [[C]], label %[[LARGE:.*]], label %[[SMALL:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: ret i1 true
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp ole float %abs, 4.000000e+00
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 3
ret i1 %r
large:
ret i1 false
}
define i1 @frexp_exp_ole_4_nofold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_ole_4_nofold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp ugt float [[ABS]], 4.000000e+00
; CHECK-NEXT: br i1 [[C]], label %[[LARGE:.*]], label %[[SMALL:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: [[FX:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[E:%.*]] = extractvalue { float, i32 } [[FX]], 1
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[E]], 3
; CHECK-NEXT: ret i1 [[R]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp ole float %abs, 4.000000e+00
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 2
ret i1 %r
large:
ret i1 false
}
; K = 3.0, above 1.0 but not a power of two: strict and non-strict give the
; same maximum exponent 2 (no power-of-two adjustment).
define i1 @frexp_exp_olt_3_fold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_3_fold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 3.000000e+00
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: ret i1 true
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 3.000000e+00
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 2
ret i1 %r
large:
ret i1 false
}
define i1 @frexp_exp_olt_3_nofold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_olt_3_nofold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 3.000000e+00
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: [[FX:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[E:%.*]] = extractvalue { float, i32 } [[FX]], 1
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[E]], 2
; CHECK-NEXT: ret i1 [[R]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 3.000000e+00
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 1
ret i1 %r
large:
ret i1 false
}
define i1 @frexp_exp_ole_3_fold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_ole_3_fold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp ugt float [[ABS]], 3.000000e+00
; CHECK-NEXT: br i1 [[C]], label %[[LARGE:.*]], label %[[SMALL:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: ret i1 true
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp ole float %abs, 3.000000e+00
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 2
ret i1 %r
large:
ret i1 false
}
define i1 @frexp_exp_ole_3_nofold(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_ole_3_nofold(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp ugt float [[ABS]], 3.000000e+00
; CHECK-NEXT: br i1 [[C]], label %[[LARGE:.*]], label %[[SMALL:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: [[FX:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[E:%.*]] = extractvalue { float, i32 } [[FX]], 1
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[E]], 2
; CHECK-NEXT: ret i1 [[R]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp ole float %abs, 3.000000e+00
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 1
ret i1 %r
large:
ret i1 false
}
; With DAZ can't prove the input isn't 0.
define i1 @frexp_exp_denormal_dynamic_nofold(float nofpclass(nan inf zero) %x) #0 {
; CHECK-LABEL: define i1 @frexp_exp_denormal_dynamic_nofold(
; CHECK-SAME: float nofpclass(nan inf zero) [[X:%.*]]) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[ABS]], 2.500000e-01
; CHECK-NEXT: br i1 [[C]], label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: [[FX:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[E:%.*]] = extractvalue { float, i32 } [[FX]], 1
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[E]], -1
; CHECK-NEXT: ret i1 [[R]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp olt float %abs, 2.500000e-01
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, -2
ret i1 %r
large:
ret i1 false
}
; Test degenerate negative constant against fabs
define i1 @frexp_exp_negative_limit(float nofpclass(nan inf) %x) {
; CHECK-LABEL: define i1 @frexp_exp_negative_limit(
; CHECK-SAME: float nofpclass(nan inf) [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br i1 true, label %[[SMALL:.*]], label %[[LARGE:.*]]
; CHECK: [[SMALL]]:
; CHECK-NEXT: [[FX:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
; CHECK-NEXT: [[E:%.*]] = extractvalue { float, i32 } [[FX]], 1
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[E]], 2
; CHECK-NEXT: ret i1 [[R]]
; CHECK: [[LARGE]]:
; CHECK-NEXT: ret i1 false
;
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp ogt float %abs, -2.000000e+00
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 1
ret i1 %r
large:
ret i1 false
}
attributes #0 = { denormal_fpenv(dynamic) }