Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion llvm/lib/Analysis/ConstantFolding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2008,7 +2008,9 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
Name == "log10f" || Name == "logb" || Name == "logbf" ||
Name == "log1p" || Name == "log1pf";
case 'n':
return Name == "nearbyint" || Name == "nearbyintf";
return Name == "nearbyint" || Name == "nearbyintf" || Name == "nextafter" ||
Name == "nextafterf" || Name == "nexttoward" ||
Name == "nexttowardf";
case 'p':
return Name == "pow" || Name == "powf";
case 'r':
Expand Down Expand Up @@ -3174,6 +3176,53 @@ static Constant *evaluateCompare(const APFloat &Op1, const APFloat &Op2,
return nullptr;
}

/// Returns the first NaN in the operand list if it exists, preserving the NaN
/// payload if possible. Returns nullptr if no NaNs are in the list.
static Constant *TryConstantFoldNaN(ArrayRef<APFloat> Operands,
const Type *RetTy) {
assert(RetTy != nullptr);
for (const APFloat &Op : Operands) {
if (Op.isNaN()) {
bool Unused;
APFloat Ret(Op);
Ret.convert(RetTy->getFltSemantics(), detail::rmNearestTiesToEven,
&Unused);
return ConstantFP::get(RetTy->getContext(), Ret);
}
}
return nullptr;
}

static Constant *ConstantFoldNextToward(const APFloat &Op0, const APFloat &Op1,
const Type *RetTy,
bool *WouldSetErrno) {
assert(RetTy != nullptr);
*WouldSetErrno = false;

Constant *RetNaN = TryConstantFoldNaN({Op0, Op1}, RetTy);
if (RetNaN != nullptr) {
return RetNaN;
}

// Recall that the second argument of nexttoward is always a long double,
// so we may need to promote the first argument for comparisons to be valid.
bool LosesInfo;
APFloat PromotedOp0(Op0);
PromotedOp0.convert(Op1.getSemantics(), detail::rmNearestTiesToEven,
&LosesInfo);
assert(!LosesInfo && "Unexpected lossy promotion");

if (PromotedOp0 == Op1)
return ConstantFP::get(RetTy->getContext(), Op0);

APFloat Next(Op0);
Next.next(/*nextDown=*/PromotedOp0 > Op1);
const bool DidOverflow = !Op0.isInfinity() && Next.isInfinity();
*WouldSetErrno = Next.isZero() || Next.isDenormal() || DidOverflow;

return ConstantFP::get(RetTy->getContext(), Next);
}

static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty,
ArrayRef<Constant *> Operands,
const TargetLibraryInfo *TLI) {
Expand Down Expand Up @@ -3233,6 +3282,14 @@ static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty,
if (TLI->has(Func))
return ConstantFoldBinaryFP(atan2, Op1V, Op2V, Ty);
break;
case LibFunc_nextafter:
case LibFunc_nextafterf:
case LibFunc_nexttoward:
case LibFunc_nexttowardf:
if (TLI->has(Func)) {
bool Unused;
return ConstantFoldNextToward(Op1V, Op2V, Ty, &Unused);
}
}

return nullptr;
Expand Down Expand Up @@ -4685,6 +4742,16 @@ bool llvm::isMathLibCallNoop(const CallBase *Call,
// may occur, so allow for that possibility.
return !Op0.isZero() || !Op1.isZero();

case LibFunc_nextafter:
case LibFunc_nextafterf:
case LibFunc_nextafterl:
case LibFunc_nexttoward:
case LibFunc_nexttowardf:
case LibFunc_nexttowardl: {
bool WouldSetErrno;
ConstantFoldNextToward(Op0, Op1, F->getReturnType(), &WouldSetErrno);
return !WouldSetErrno;
}
default:
break;
}
Expand Down
226 changes: 226 additions & 0 deletions llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: cat %S/floating-point-constants.ll %s | opt -passes=instcombine -S | FileCheck %s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
; RUN: cat %S/floating-point-constants.ll %s | opt -passes=instcombine -S | FileCheck %s
; RUN: opt -S -passes=instcombine < %s | FileCheck %s

There's no reason to use cat in tests

Copy link
Contributor Author

@sivakusayan sivakusayan Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it that way so I didn't have to copy-paste floating point constants everywhere, and could instead pull in the values from the floating-point-constants.ll file. I couldn't really find an established pattern for it, so I asked for suggestions in the LLVM Discord. Do you know of a better way to do this?

If it feels too strange, I could copy what other lit tests do. I see other tests like ilogb folding hardcode the constants for example.


declare double @nextafter(double, double) #0
declare float @nextafterf(float, float) #0

attributes #0 = { willreturn memory(errnomem: write) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this to the end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!


define double @nextafter_up_direction() {
; CHECK-LABEL: define double @nextafter_up_direction() {
; CHECK-NEXT: ret double 0x3FF0000000000001
;
%next = call double @nextafter(double 1.0, double 2.0)
ret double %next
}

define float @nextafterf_up_direction() {
; CHECK-LABEL: define float @nextafterf_up_direction() {
; CHECK-NEXT: ret float 0x3FF0000020000000
;
%next = call float @nextafterf(float 1.0, float 2.0)
ret float %next
}

define double @nextafter_down_direction() {
; CHECK-LABEL: define double @nextafter_down_direction() {
; CHECK-NEXT: ret double 0x3FEFFFFFFFFFFFFF
;
%next = call double @nextafter(double 1.0, double 0.0)
ret double %next
}

define float @nextafterf_down_direction() {
; CHECK-LABEL: define float @nextafterf_down_direction() {
; CHECK-NEXT: ret float 0x3FEFFFFFE0000000
;
%next = call float @nextafterf(float 1.0, float 0.0)
ret float %next
}

define double @nextafter_equal_args() {
; CHECK-LABEL: define double @nextafter_equal_args() {
; CHECK-NEXT: ret double 1.000000e+00
;
%next = call double @nextafter(double 1.0, double 1.0)
ret double %next
}

define float @nextafterf_equal_args() {
; CHECK-LABEL: define float @nextafterf_equal_args() {
; CHECK-NEXT: ret float 1.000000e+00
;
%next = call float @nextafterf(float 1.0, float 1.0)
ret float %next
}

define double @nextafter_nan_with_payload() {
; CHECK-LABEL: define double @nextafter_nan_with_payload() {
; CHECK-NEXT: ret double 0x7FF8000000000001
;
%nan = load double, double* @dbl_nan
%tmp1 = bitcast double %nan to i64
%tmp2 = or i64 %tmp1, 1
%nan_with_payload = bitcast i64 %tmp2 to double
%next = call double @nextafter(double %nan_with_payload, double 1.0)
ret double %next

}

define float @nextafterf_nan_with_payload() {
; CHECK-LABEL: define float @nextafterf_nan_with_payload() {
; CHECK-NEXT: ret float 0x7FF8000020000000
;
%nan = load float, float* @flt_nan
%tmp1 = bitcast float %nan to i32
%tmp2 = or i32 %tmp1, 1
%nan_with_payload = bitcast i32 %tmp2 to float
%next = call float @nextafterf(float %nan_with_payload, float 1.0)
ret float %next
}

define double @nextafter_pos_overflow () {
; CHECK-LABEL: define double @nextafter_pos_overflow() {
; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 0x7FEFFFFFFFFFFFFF, double 0x7FF0000000000000)
; CHECK-NEXT: ret double 0x7FF0000000000000
;
%arg1 = load double, double* @dbl_pos_max
%arg2 = load double, double* @dbl_pos_infinity
%next = call double @nextafter(double %arg1, double %arg2)
ret double %next
}

define float @nextafterf_pos_overflow() {
; CHECK-LABEL: define float @nextafterf_pos_overflow() {
; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0x47EFFFFFE0000000, float 0x7FF0000000000000)
; CHECK-NEXT: ret float 0x7FF0000000000000
;
%arg1 = load float, float* @flt_pos_max
%arg2 = load float, float* @flt_pos_infinity
%next = call float @nextafterf(float %arg1, float %arg2)
ret float %next
}

define double @nextafter_neg_overflow() {
; CHECK-LABEL: define double @nextafter_neg_overflow() {
; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 0xFFEFFFFFFFFFFFFF, double 0xFFF0000000000000)
; CHECK-NEXT: ret double 0xFFF0000000000000
;
%arg1 = load double, double* @dbl_neg_max
%arg2 = load double, double* @dbl_neg_infinity
%next = call double @nextafter(double %arg1, double %arg2)
ret double %next
}

define float @nextafterf_neg_overflow() {
; CHECK-LABEL: define float @nextafterf_neg_overflow() {
; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0xC7EFFFFFE0000000, float 0xFFF0000000000000)
; CHECK-NEXT: ret float 0xFFF0000000000000
;
%arg1 = load float, float* @flt_neg_max
%arg2 = load float, float* @flt_neg_infinity
%next = call float @nextafterf(float %arg1, float %arg2)
ret float %next
}

define double @nextafter_zero_from_above() {
; CHECK-LABEL: define double @nextafter_zero_from_above() {
; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 4.940660e-324, double 0.000000e+00)
; CHECK-NEXT: ret double 0.000000e+00
;
%arg = load double, double* @dbl_pos_min_subnormal
%next = call double @nextafter(double %arg, double 0.0)
ret double %next
}

define float @nextafterf_zero_from_above() {
; CHECK-LABEL: define float @nextafterf_zero_from_above() {
; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0x36A0000000000000, float 0.000000e+00)
; CHECK-NEXT: ret float 0.000000e+00
;
%arg = load float, float* @flt_pos_min_subnormal
%next = call float @nextafterf(float %arg, float 0.0)
ret float %next
}

define double @nextafter_zero_from_below() {
; CHECK-LABEL: define double @nextafter_zero_from_below() {
; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double -4.940660e-324, double 0.000000e+00)
; CHECK-NEXT: ret double -0.000000e+00
;
%arg = load double, double* @dbl_neg_min_subnormal
%next = call double @nextafter(double %arg, double 0.0)
ret double %next
}

define float @nextafterf_zero_from_below() {
; CHECK-LABEL: define float @nextafterf_zero_from_below() {
; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0xB6A0000000000000, float 0.000000e+00)
; CHECK-NEXT: ret float -0.000000e+00
;
%arg = load float, float* @flt_neg_min_subnormal
%next = call float @nextafterf(float %arg, float 0.0)
ret float %next
}

define double @nextafter_subnormal() {
; CHECK-LABEL: define double @nextafter_subnormal() {
; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 4.940660e-324, double 0x7FF0000000000000)
; CHECK-NEXT: ret double 9.881310e-324
;
%subnormal = load double, double* @dbl_pos_min_subnormal
%infinity = load double, double* @dbl_pos_infinity
%next = call double @nextafter(double %subnormal, double %infinity)
ret double %next
}

define float @nextafterf_subnormal() {
; CHECK-LABEL: define float @nextafterf_subnormal() {
; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0x36A0000000000000, float 0x7FF0000000000000)
; CHECK-NEXT: ret float 0x36B0000000000000
;
%subnormal = load float, float* @flt_pos_min_subnormal
%infinity = load float, float* @flt_pos_infinity
%next = call float @nextafterf(float %subnormal, float %infinity)
ret float %next
}

define double @nextafter_poison() {
; CHECK-LABEL: define double @nextafter_poison() {
; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double poison, double 1.000000e+00)
; CHECK-NEXT: ret double [[NEXT]]
;
%next = call double @nextafter(double poison, double 1.0)
ret double %next
}

define double @nextafterf_poison() {
; CHECK-LABEL: define double @nextafterf_poison() {
; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafterf(float poison, float 1.000000e+00)
; CHECK-NEXT: ret double [[NEXT]]
;
%next = call double @nextafterf(float poison, float 1.0)
ret double %next
}

define double @nextafter_subnormal_readnone() {
; CHECK-LABEL: define double @nextafter_subnormal_readnone() {
; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 4.940660e-324, double 0x7FF0000000000000) #[[ATTR1:[0-9]+]]
; CHECK-NEXT: ret double 9.881310e-324
;
%subnormal = load double, double* @dbl_pos_min_subnormal
%infinity = load double, double* @dbl_pos_infinity
%next = call double @nextafter(double %subnormal, double %infinity) readnone
ret double %next
}

define float @nextafterf_subnormal_readnone() {
; CHECK-LABEL: define float @nextafterf_subnormal_readnone() {
; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0x36A0000000000000, float 0x7FF0000000000000) #[[ATTR1]]
; CHECK-NEXT: ret float 0x36B0000000000000
;
%subnormal = load float, float* @flt_pos_min_subnormal
%infinity = load float, float* @flt_pos_infinity
%next = call float @nextafterf(float %subnormal, float %infinity) readnone
ret float %next
}
Loading