Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
13 changes: 13 additions & 0 deletions llvm/include/llvm/ADT/APFloat.h
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,19 @@ class APFloat : public APFloatBase {
/// \param Semantics - type float semantics
LLVM_ABI static APFloat getAllOnesValue(const fltSemantics &Semantics);

/// Returns a copy of this APFloat with the requested semantics.
/// The requested semantics should be equal to or stronger
/// than the semantics of the current instance.
APFloat getPromoted(const fltSemantics &Sem) const {
assert(isRepresentableBy(this->getSemantics(), Sem) &&
"Target semantics will lose information.");
APFloat Val(*this);
bool LosesInfo;
Val.convert(Sem, rmNearestTiesToEven, &LosesInfo);
assert(!LosesInfo);
return Val;
}

/// Returns true if the given semantics has actual significand.
///
/// \param Sem - type float semantics
Expand Down
52 changes: 51 additions & 1 deletion llvm/lib/Analysis/ConstantFolding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1996,7 +1996,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";
Comment on lines +1999 to +2001
Copy link
Member

Choose a reason for hiding this comment

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

BTW, all of the surrounding code can be simplified with llvm::is_contained, but that's for another PR

case 'p':
return Name == "pow" || Name == "powf";
case 'r':
Expand Down Expand Up @@ -3221,6 +3223,35 @@ 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)) {
if (Op1V.isNaN())
return ConstantFP::get(Ty->getContext(), Op1V);
if (Op2V.isNaN()) {
// IEEE 754, 6.5.4 recommends the inclusion of diagnostic
// information if the NaN payload can't be preserved. Should
// we do something special here?
const bool SemEqual = &Op2V.getSemantics() == &Ty->getFltSemantics();
const APFloat Ret =
SemEqual ? Op2V : APFloat::getNaN(Ty->getFltSemantics());
return ConstantFP::get(Ty->getContext(), Ret);
}

// The two arguments of nexttoward can have differing semantics.
// We need to convert both arguments to the same semantics so
// we can do comparisons.
const APFloat PromotedOp1V = Op1V.getPromoted(APFloat::IEEEquad());
const APFloat PromotedOp2V = Op2V.getPromoted(APFloat::IEEEquad());
if (PromotedOp1V == PromotedOp2V)
return ConstantFP::get(Ty->getContext(), Op1V);

APFloat Next(Op1V);
Next.next(/*nextDown=*/PromotedOp1V > PromotedOp2V);
return ConstantFP::get(Ty->getContext(), Next);
}
}

return nullptr;
Expand Down Expand Up @@ -4655,6 +4686,25 @@ 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: {
// The two arguments of nexttoward can have differing semantics.
// We need to convert both arguments to the same semantics so
// we can do comparisons.
const APFloat PromotedOp0 = Op0.getPromoted(APFloat::IEEEquad());
const APFloat PromotedOp1 = Op1.getPromoted(APFloat::IEEEquad());
if (PromotedOp0 == PromotedOp1)
return true;

APFloat Next(Op0);
Next.next(/*nextDown=*/PromotedOp0 > PromotedOp1);
const bool DidOverflow = Op0.isLargest() && Next.isInfinity();
return !Next.isZero() && !Next.isDenormal() && !DidOverflow;
}
default:
break;
}
Expand Down
212 changes: 212 additions & 0 deletions llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
; 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

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

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

; ==================
; nextafter tests
; ==================

define double @nextafter_can_constant_fold_up_direction() {
; CHECK-LABEL: define double @nextafter_can_constant_fold_up_direction() {
; CHECK-NEXT: ret double 0x3FF0000000000001
;
%next = call double @nextafter(double noundef 1.0, double noundef 2.0)
ret double %next
}
define double @nextafter_can_constant_fold_down_direction() {
; CHECK-LABEL: define double @nextafter_can_constant_fold_down_direction() {
; CHECK-NEXT: ret double 0x3FEFFFFFFFFFFFFF
;
%next = call double @nextafter(double noundef 1.0, double noundef 0.0)
ret double %next
}
define double @nextafter_can_constant_fold_equal_args() {
; CHECK-LABEL: define double @nextafter_can_constant_fold_equal_args() {
; CHECK-NEXT: ret double 1.000000e+00
;
%next = call double @nextafter(double noundef 1.0, double noundef 1.0)
ret double %next
}
define double @nextafter_can_constant_fold_with_nan_arg() {
; CHECK-LABEL: define double @nextafter_can_constant_fold_with_nan_arg() {
; CHECK-NEXT: ret double 0x7FF8000000000000
;
%arg = load double, double* @dbl_nan
%next = call double @nextafter(double 1.0, double %arg)
ret double %next
}
define double @nextafter_constant_fold_propagates_nan_payload() {
; CHECK-LABEL: define double @nextafter_constant_fold_propagates_nan_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 double @nextafter_not_marked_dead_on_pos_overflow () {
; CHECK-LABEL: define double @nextafter_not_marked_dead_on_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 double @nextafter_not_marked_dead_on_neg_overflow() {
; CHECK-LABEL: define double @nextafter_not_marked_dead_on_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 double @nextafter_not_marked_dead_on_zero_from_above() {
; CHECK-LABEL: define double @nextafter_not_marked_dead_on_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 double @nextafter_not_marked_dead_on_zero_from_below() {
; CHECK-LABEL: define double @nextafter_not_marked_dead_on_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 double @nextafter_not_marked_dead_on_subnormal() {
; CHECK-LABEL: define double @nextafter_not_marked_dead_on_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 double @nextafter_not_marked_dead_on_poison() {
; CHECK-LABEL: define double @nextafter_not_marked_dead_on_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 @nextafter_marked_dead_when_readnone() {
; CHECK-LABEL: define double @nextafter_marked_dead_when_readnone() {
; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 4.940660e-324, double 1.000000e+00) #[[ATTR1:[0-9]+]]
; CHECK-NEXT: ret double 9.881310e-324
;
%subnormal = load double, double* @dbl_pos_min_subnormal
%next = call double @nextafter(double %subnormal, double 1.0) readnone
ret double %next
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm a bit confused why adding readnone didn't kill the call to nextafter, but I see it didn't do that either for ilogb so maybe this is expected for now?


; ==================
; nextafterf tests
; ==================

define float @nextafterf_can_constant_fold_up_direction() {
; CHECK-LABEL: define float @nextafterf_can_constant_fold_up_direction() {
; CHECK-NEXT: ret float 0x3FF0000020000000
;
%next = call float @nextafterf(float noundef 1.0, float noundef 2.0)
ret float %next
}
define float @nextafterf_can_constant_fold_down_direction() {
; CHECK-LABEL: define float @nextafterf_can_constant_fold_down_direction() {
; CHECK-NEXT: ret float 0x3FEFFFFFE0000000
;
%next = call float @nextafterf(float noundef 1.0, float noundef 0.0)
ret float %next
}
define float @nextafterf_can_constant_fold_equal_args() {
; CHECK-LABEL: define float @nextafterf_can_constant_fold_equal_args() {
; CHECK-NEXT: ret float 1.000000e+00
;
%next = call float @nextafterf(float noundef 1.0, float noundef 1.0)
ret float %next
}
define float @nextafterf_can_constant_fold_with_nan_arg() {
; CHECK-LABEL: define float @nextafterf_can_constant_fold_with_nan_arg() {
; CHECK-NEXT: ret float 0x7FF8000000000000
;
%arg = load float, float* @flt_nan
%next = call float @nextafterf(float 1.0, float %arg)
ret float %next
}
define float @nextafterf_constant_fold_propagates_nan_payload() {
; CHECK-LABEL: define float @nextafterf_constant_fold_propagates_nan_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 float @nextafterf_not_marked_dead_on_pos_overflow() {
; CHECK-LABEL: define float @nextafterf_not_marked_dead_on_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 float @nextafterf_not_marked_dead_on_neg_overflow() {
; CHECK-LABEL: define float @nextafterf_not_marked_dead_on_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 float @nextafterf_not_marked_dead_on_zero_from_above() {
; CHECK-LABEL: define float @nextafterf_not_marked_dead_on_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 float @nextafterf_not_marked_dead_on_zero_from_below() {
; CHECK-LABEL: define float @nextafterf_not_marked_dead_on_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 float @nextafterf_not_marked_dead_on_subnormal() {
; CHECK-LABEL: define float @nextafterf_not_marked_dead_on_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
}
Loading
Loading