-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[ConstantFolding] Add constant folding support for nextafter/nexttoward #167324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ConstantFolding] Add constant folding support for nextafter/nexttoward #167324
Conversation
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
cc01e5a to
5b9786f
Compare
5b9786f to
e94017f
Compare
|
@llvm/pr-subscribers-llvm-analysis Author: Sayan Sivakumaran (sivakusayan) ChangesFixes issue #74368. This patch enables us to constant fold Apologies for the large patch, the majority of it are Patch is 38.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167324.diff 7 Files Affected:
diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index 82ac9a3a1ef80..32e5c1aafc245 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -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
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index da32542cf7870..fadd3e7896c3a 100755
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -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";
case 'p':
return Name == "pow" || Name == "powf";
case 'r':
@@ -3221,6 +3223,26 @@ 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() || Op2V.isNaN()) {
+ return ConstantFP::get(Ty->getContext(),
+ APFloat::getNaN(Ty->getFltSemantics()));
+ }
+
+ APFloat PromotedOp1V = Op1V.getPromoted(APFloat::IEEEquad());
+ 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;
@@ -4655,6 +4677,22 @@ 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: {
+ APFloat PromotedOp0 = Op0.getPromoted(APFloat::IEEEquad());
+ APFloat PromotedOp1 = Op1.getPromoted(APFloat::IEEEquad());
+ if (PromotedOp0 == PromotedOp1)
+ return true;
+
+ APFloat Next(Op0);
+ Next.next(/*nextDown=*/PromotedOp0 > PromotedOp1);
+ bool DidOverflow = Op0.isLargest() && Next.isInfinity();
+ return !Next.isZero() && !Next.isDenormal() && !DidOverflow;
+ }
default:
break;
}
diff --git a/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll b/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll
new file mode 100644
index 0000000000000..9af0a5aeba871
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll
@@ -0,0 +1,171 @@
+; 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_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
+}
+
+; ==================
+; 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_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
+}
diff --git a/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll
new file mode 100644
index 0000000000000..5f044241fbce8
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll
@@ -0,0 +1,187 @@
+; 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 @nexttoward(double noundef, fp128 noundef) #0
+declare float @nexttowardf(float noundef, fp128 noundef) #0
+
+attributes #0 = { willreturn memory(errnomem: write) }
+
+; ==================
+; nexttoward tests
+; ==================
+
+define double @nexttoward_can_constant_fold_up_direction() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_up_direction() {
+; CHECK-NEXT: ret double 0x3FF0000000000001
+;
+ %arg = fpext double 2.0 to fp128
+ %next = call double @nexttoward(double noundef 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_can_constant_fold_down_direction() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_down_direction() {
+; CHECK-NEXT: ret double 0x3FEFFFFFFFFFFFFF
+;
+ %arg = fpext double 0.0 to fp128
+ %next = call double @nexttoward(double noundef 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_can_constant_fold_equal_args() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_equal_args() {
+; CHECK-NEXT: ret double 1.000000e+00
+;
+ %arg = fpext double 1.0 to fp128
+ %next = call double @nexttoward(double 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_can_constant_fold_with_nan_arg() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_with_nan_arg() {
+; CHECK-NEXT: ret double 0x7FF8000000000000
+;
+ %nan = load double, double* @dbl_nan
+ %arg = fpext double %nan to fp128
+ %next = call double @nexttoward(double 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_pos_overflow() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_pos_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0x7FEFFFFFFFFFFFFF, fp128 0xL00000000000000007FFF000000000000)
+; CHECK-NEXT: ret double 0x7FF0000000000000
+;
+ %pos_max = load double, double* @dbl_pos_max
+ %pos_inf = load double, double* @dbl_pos_infinity
+ %ext_pos_inf = fpext double %pos_inf to fp128
+ %next = call double @nexttoward(double %pos_max, fp128 %ext_pos_inf)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_neg_overflow() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_neg_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0xFFEFFFFFFFFFFFFF, fp128 0xL0000000000000000FFFF000000000000)
+; CHECK-NEXT: ret double 0xFFF0000000000000
+;
+ %neg_max = load double, double* @dbl_neg_max
+ %neg_inf = load double, double* @dbl_neg_infinity
+ %ext_neg_inf = fpext double %neg_inf to fp128
+ %next = call double @nexttoward(double %neg_max, fp128 %ext_neg_inf)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_zero_from_above() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_zero_from_above() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret double 0.000000e+00
+;
+ %subnormal = load double, double* @dbl_pos_min_subnormal
+ %zero = fpext double 0.0 to fp128
+ %next = call double @nexttoward(double %subnormal, fp128 %zero)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_zero_from_below() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_zero_from_below() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double -4.940660e-324, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret double -0.000000e+00
+;
+ %subnormal = load double, double* @dbl_neg_min_subnormal
+ %zero = fpext double 0.0 to fp128
+ %next = call double @nexttoward(double %subnormal, fp128 %zero)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_subnormal() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_subnormal() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, fp128 0xL00000000000000003FFF000000000000)
+; CHECK-NEXT: ret double 9.881310e-324
+;
+ %subnormal = load double, double* @dbl_pos_min_subnormal
+ %target = fpext double 1.0 to fp128
+ %next = call double @nexttoward(double %subnormal, fp128 %target)
+ ret double %next
+}
+
+; ==================
+; nexttowardf tests
+; ==================
+
+define float @nexttowardf_can_constant_fold_up_direction() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_up_direction() {
+; CHECK-NEXT: ret float 0x3FF0000020000000
+;
+ %arg = fpext float 2.0 to fp128
+ %next = call float @nexttowardf(float noundef 1.0, fp128 %arg)
+ ret float %next
+}
+define float @nexttowardf_can_constant_fold_down_direction() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_down_direction() {
+; CHECK-NEXT: ret float 0x3FEFFFFFE0000000
+;
+ %arg = fpext float 0.0 to fp128
+ %next = call float @nexttowardf(float noundef 1.0, fp128 %arg)
+ ret float %next
+}
+define float @nexttowardf_can_constant_fold_equal_args() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_equal_args() {
+; CHECK-NEXT: ret float 1.000000e+00
+;
+ %arg = fpext float 1.0 to fp128
+ %next = call float @nexttowardf(float 1.0, fp128 %arg)
+ ret float %next
+}
+define float @nexttowardf_can_constant_fold_with_nan_arg() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_with_nan_arg() {
+; CHECK-NEXT: ret float 0x7FF8000000000000
+;
+ %nan = load float, float* @flt_nan
+ %ext_nan = fpext float %nan to fp128
+ %next = call float @nexttowardf(float 1.0, fp128 %ext_nan)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_pos_overflow () {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_pos_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x47EFFFFFE0000000, fp128 0xL00000000000000007FFF000000000000)
+; CHECK-NEXT: ret float 0x7FF0000000000000
+;
+ %pos_max = load float, float* @flt_pos_max
+ %pos_inf = load float, float* @flt_pos_infinity
+ %ext_pos_inf = fpext float %pos_inf to fp128
+ %next = call float @nexttowardf(float %pos_max, fp128 %ext_pos_inf)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_neg_overflow() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_neg_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0xC7EFFFFFE0000000, fp128 0xL0000000000000000FFFF000000000000)
+; CHECK-NEXT: ret float 0xFFF0000000000000
+;
+ %neg_max = load float, float* @flt_neg_max
+ %neg_inf = load float, float* @flt_neg_infinity
+ %ext_neg_inf = fpext float %neg_inf to fp128
+ %next = call float @nexttowardf(float %neg_max, fp128 %ext_neg_inf)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_zero_from_above() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_zero_from_above() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret float 0.000000e+00
+;
+ %min_subnormal = load float, float* @flt_pos_min_subnormal
+ %zero = fpext float 0.0 to fp128
+ %next = call float @nexttowardf(float %min_subnormal, fp128 %zero)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_zero_from_below() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_zero_from_below() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0xB6A0000000000000, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret float -0.000000e+00
+;
+ %min_subnormal = load float, float* @flt_neg_min_subnormal
+ %zero = fpext float 0.0 to fp128
+ %next = call float @nexttowardf(float %min_subnormal, fp128 %zero)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_subnormal() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_subnormal() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, fp128 0xL00000000000000003FFF000000000000)
+; CHECK-NEXT: ret float 0x36B0000000000000
+;
+ %subnormal = load float, float* @flt_pos_min_subnormal
+ %target = fpext float 1.0 to fp128
+ %next = call float @nexttowardf(float %subnormal, fp128 %target)
+ ret float %next
+}
diff --git a/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll
new file mode 100644
index 0000000000000..30da81b1bd01f
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll
@@ -0,0 +1,187 @@
+; 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 @nexttoward(double noundef, ppc_fp128 noundef) #0
+declare float @nexttowardf(float noundef, ppc_fp128 noundef) #0
+
+attribut...
[truncated]
|
|
@llvm/pr-subscribers-llvm-transforms Author: Sayan Sivakumaran (sivakusayan) ChangesFixes issue #74368. This patch enables us to constant fold Apologies for the large patch, the majority of it are Patch is 38.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167324.diff 7 Files Affected:
diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index 82ac9a3a1ef80..32e5c1aafc245 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -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
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index da32542cf7870..fadd3e7896c3a 100755
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -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";
case 'p':
return Name == "pow" || Name == "powf";
case 'r':
@@ -3221,6 +3223,26 @@ 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() || Op2V.isNaN()) {
+ return ConstantFP::get(Ty->getContext(),
+ APFloat::getNaN(Ty->getFltSemantics()));
+ }
+
+ APFloat PromotedOp1V = Op1V.getPromoted(APFloat::IEEEquad());
+ 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;
@@ -4655,6 +4677,22 @@ 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: {
+ APFloat PromotedOp0 = Op0.getPromoted(APFloat::IEEEquad());
+ APFloat PromotedOp1 = Op1.getPromoted(APFloat::IEEEquad());
+ if (PromotedOp0 == PromotedOp1)
+ return true;
+
+ APFloat Next(Op0);
+ Next.next(/*nextDown=*/PromotedOp0 > PromotedOp1);
+ bool DidOverflow = Op0.isLargest() && Next.isInfinity();
+ return !Next.isZero() && !Next.isDenormal() && !DidOverflow;
+ }
default:
break;
}
diff --git a/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll b/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll
new file mode 100644
index 0000000000000..9af0a5aeba871
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll
@@ -0,0 +1,171 @@
+; 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_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
+}
+
+; ==================
+; 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_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
+}
diff --git a/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll
new file mode 100644
index 0000000000000..5f044241fbce8
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll
@@ -0,0 +1,187 @@
+; 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 @nexttoward(double noundef, fp128 noundef) #0
+declare float @nexttowardf(float noundef, fp128 noundef) #0
+
+attributes #0 = { willreturn memory(errnomem: write) }
+
+; ==================
+; nexttoward tests
+; ==================
+
+define double @nexttoward_can_constant_fold_up_direction() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_up_direction() {
+; CHECK-NEXT: ret double 0x3FF0000000000001
+;
+ %arg = fpext double 2.0 to fp128
+ %next = call double @nexttoward(double noundef 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_can_constant_fold_down_direction() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_down_direction() {
+; CHECK-NEXT: ret double 0x3FEFFFFFFFFFFFFF
+;
+ %arg = fpext double 0.0 to fp128
+ %next = call double @nexttoward(double noundef 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_can_constant_fold_equal_args() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_equal_args() {
+; CHECK-NEXT: ret double 1.000000e+00
+;
+ %arg = fpext double 1.0 to fp128
+ %next = call double @nexttoward(double 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_can_constant_fold_with_nan_arg() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_with_nan_arg() {
+; CHECK-NEXT: ret double 0x7FF8000000000000
+;
+ %nan = load double, double* @dbl_nan
+ %arg = fpext double %nan to fp128
+ %next = call double @nexttoward(double 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_pos_overflow() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_pos_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0x7FEFFFFFFFFFFFFF, fp128 0xL00000000000000007FFF000000000000)
+; CHECK-NEXT: ret double 0x7FF0000000000000
+;
+ %pos_max = load double, double* @dbl_pos_max
+ %pos_inf = load double, double* @dbl_pos_infinity
+ %ext_pos_inf = fpext double %pos_inf to fp128
+ %next = call double @nexttoward(double %pos_max, fp128 %ext_pos_inf)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_neg_overflow() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_neg_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0xFFEFFFFFFFFFFFFF, fp128 0xL0000000000000000FFFF000000000000)
+; CHECK-NEXT: ret double 0xFFF0000000000000
+;
+ %neg_max = load double, double* @dbl_neg_max
+ %neg_inf = load double, double* @dbl_neg_infinity
+ %ext_neg_inf = fpext double %neg_inf to fp128
+ %next = call double @nexttoward(double %neg_max, fp128 %ext_neg_inf)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_zero_from_above() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_zero_from_above() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret double 0.000000e+00
+;
+ %subnormal = load double, double* @dbl_pos_min_subnormal
+ %zero = fpext double 0.0 to fp128
+ %next = call double @nexttoward(double %subnormal, fp128 %zero)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_zero_from_below() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_zero_from_below() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double -4.940660e-324, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret double -0.000000e+00
+;
+ %subnormal = load double, double* @dbl_neg_min_subnormal
+ %zero = fpext double 0.0 to fp128
+ %next = call double @nexttoward(double %subnormal, fp128 %zero)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_subnormal() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_subnormal() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, fp128 0xL00000000000000003FFF000000000000)
+; CHECK-NEXT: ret double 9.881310e-324
+;
+ %subnormal = load double, double* @dbl_pos_min_subnormal
+ %target = fpext double 1.0 to fp128
+ %next = call double @nexttoward(double %subnormal, fp128 %target)
+ ret double %next
+}
+
+; ==================
+; nexttowardf tests
+; ==================
+
+define float @nexttowardf_can_constant_fold_up_direction() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_up_direction() {
+; CHECK-NEXT: ret float 0x3FF0000020000000
+;
+ %arg = fpext float 2.0 to fp128
+ %next = call float @nexttowardf(float noundef 1.0, fp128 %arg)
+ ret float %next
+}
+define float @nexttowardf_can_constant_fold_down_direction() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_down_direction() {
+; CHECK-NEXT: ret float 0x3FEFFFFFE0000000
+;
+ %arg = fpext float 0.0 to fp128
+ %next = call float @nexttowardf(float noundef 1.0, fp128 %arg)
+ ret float %next
+}
+define float @nexttowardf_can_constant_fold_equal_args() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_equal_args() {
+; CHECK-NEXT: ret float 1.000000e+00
+;
+ %arg = fpext float 1.0 to fp128
+ %next = call float @nexttowardf(float 1.0, fp128 %arg)
+ ret float %next
+}
+define float @nexttowardf_can_constant_fold_with_nan_arg() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_with_nan_arg() {
+; CHECK-NEXT: ret float 0x7FF8000000000000
+;
+ %nan = load float, float* @flt_nan
+ %ext_nan = fpext float %nan to fp128
+ %next = call float @nexttowardf(float 1.0, fp128 %ext_nan)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_pos_overflow () {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_pos_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x47EFFFFFE0000000, fp128 0xL00000000000000007FFF000000000000)
+; CHECK-NEXT: ret float 0x7FF0000000000000
+;
+ %pos_max = load float, float* @flt_pos_max
+ %pos_inf = load float, float* @flt_pos_infinity
+ %ext_pos_inf = fpext float %pos_inf to fp128
+ %next = call float @nexttowardf(float %pos_max, fp128 %ext_pos_inf)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_neg_overflow() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_neg_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0xC7EFFFFFE0000000, fp128 0xL0000000000000000FFFF000000000000)
+; CHECK-NEXT: ret float 0xFFF0000000000000
+;
+ %neg_max = load float, float* @flt_neg_max
+ %neg_inf = load float, float* @flt_neg_infinity
+ %ext_neg_inf = fpext float %neg_inf to fp128
+ %next = call float @nexttowardf(float %neg_max, fp128 %ext_neg_inf)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_zero_from_above() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_zero_from_above() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret float 0.000000e+00
+;
+ %min_subnormal = load float, float* @flt_pos_min_subnormal
+ %zero = fpext float 0.0 to fp128
+ %next = call float @nexttowardf(float %min_subnormal, fp128 %zero)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_zero_from_below() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_zero_from_below() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0xB6A0000000000000, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret float -0.000000e+00
+;
+ %min_subnormal = load float, float* @flt_neg_min_subnormal
+ %zero = fpext float 0.0 to fp128
+ %next = call float @nexttowardf(float %min_subnormal, fp128 %zero)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_subnormal() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_subnormal() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, fp128 0xL00000000000000003FFF000000000000)
+; CHECK-NEXT: ret float 0x36B0000000000000
+;
+ %subnormal = load float, float* @flt_pos_min_subnormal
+ %target = fpext float 1.0 to fp128
+ %next = call float @nexttowardf(float %subnormal, fp128 %target)
+ ret float %next
+}
diff --git a/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll
new file mode 100644
index 0000000000000..30da81b1bd01f
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll
@@ -0,0 +1,187 @@
+; 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 @nexttoward(double noundef, ppc_fp128 noundef) #0
+declare float @nexttowardf(float noundef, ppc_fp128 noundef) #0
+
+attribut...
[truncated]
|
|
@llvm/pr-subscribers-llvm-adt Author: Sayan Sivakumaran (sivakusayan) ChangesFixes issue #74368. This patch enables us to constant fold Apologies for the large patch, the majority of it are Patch is 38.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167324.diff 7 Files Affected:
diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index 82ac9a3a1ef80..32e5c1aafc245 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -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
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index da32542cf7870..fadd3e7896c3a 100755
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -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";
case 'p':
return Name == "pow" || Name == "powf";
case 'r':
@@ -3221,6 +3223,26 @@ 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() || Op2V.isNaN()) {
+ return ConstantFP::get(Ty->getContext(),
+ APFloat::getNaN(Ty->getFltSemantics()));
+ }
+
+ APFloat PromotedOp1V = Op1V.getPromoted(APFloat::IEEEquad());
+ 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;
@@ -4655,6 +4677,22 @@ 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: {
+ APFloat PromotedOp0 = Op0.getPromoted(APFloat::IEEEquad());
+ APFloat PromotedOp1 = Op1.getPromoted(APFloat::IEEEquad());
+ if (PromotedOp0 == PromotedOp1)
+ return true;
+
+ APFloat Next(Op0);
+ Next.next(/*nextDown=*/PromotedOp0 > PromotedOp1);
+ bool DidOverflow = Op0.isLargest() && Next.isInfinity();
+ return !Next.isZero() && !Next.isDenormal() && !DidOverflow;
+ }
default:
break;
}
diff --git a/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll b/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll
new file mode 100644
index 0000000000000..9af0a5aeba871
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll
@@ -0,0 +1,171 @@
+; 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_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
+}
+
+; ==================
+; 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_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
+}
diff --git a/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll
new file mode 100644
index 0000000000000..5f044241fbce8
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll
@@ -0,0 +1,187 @@
+; 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 @nexttoward(double noundef, fp128 noundef) #0
+declare float @nexttowardf(float noundef, fp128 noundef) #0
+
+attributes #0 = { willreturn memory(errnomem: write) }
+
+; ==================
+; nexttoward tests
+; ==================
+
+define double @nexttoward_can_constant_fold_up_direction() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_up_direction() {
+; CHECK-NEXT: ret double 0x3FF0000000000001
+;
+ %arg = fpext double 2.0 to fp128
+ %next = call double @nexttoward(double noundef 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_can_constant_fold_down_direction() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_down_direction() {
+; CHECK-NEXT: ret double 0x3FEFFFFFFFFFFFFF
+;
+ %arg = fpext double 0.0 to fp128
+ %next = call double @nexttoward(double noundef 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_can_constant_fold_equal_args() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_equal_args() {
+; CHECK-NEXT: ret double 1.000000e+00
+;
+ %arg = fpext double 1.0 to fp128
+ %next = call double @nexttoward(double 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_can_constant_fold_with_nan_arg() {
+; CHECK-LABEL: define double @nexttoward_can_constant_fold_with_nan_arg() {
+; CHECK-NEXT: ret double 0x7FF8000000000000
+;
+ %nan = load double, double* @dbl_nan
+ %arg = fpext double %nan to fp128
+ %next = call double @nexttoward(double 1.0, fp128 %arg)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_pos_overflow() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_pos_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0x7FEFFFFFFFFFFFFF, fp128 0xL00000000000000007FFF000000000000)
+; CHECK-NEXT: ret double 0x7FF0000000000000
+;
+ %pos_max = load double, double* @dbl_pos_max
+ %pos_inf = load double, double* @dbl_pos_infinity
+ %ext_pos_inf = fpext double %pos_inf to fp128
+ %next = call double @nexttoward(double %pos_max, fp128 %ext_pos_inf)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_neg_overflow() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_neg_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0xFFEFFFFFFFFFFFFF, fp128 0xL0000000000000000FFFF000000000000)
+; CHECK-NEXT: ret double 0xFFF0000000000000
+;
+ %neg_max = load double, double* @dbl_neg_max
+ %neg_inf = load double, double* @dbl_neg_infinity
+ %ext_neg_inf = fpext double %neg_inf to fp128
+ %next = call double @nexttoward(double %neg_max, fp128 %ext_neg_inf)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_zero_from_above() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_zero_from_above() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret double 0.000000e+00
+;
+ %subnormal = load double, double* @dbl_pos_min_subnormal
+ %zero = fpext double 0.0 to fp128
+ %next = call double @nexttoward(double %subnormal, fp128 %zero)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_zero_from_below() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_zero_from_below() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double -4.940660e-324, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret double -0.000000e+00
+;
+ %subnormal = load double, double* @dbl_neg_min_subnormal
+ %zero = fpext double 0.0 to fp128
+ %next = call double @nexttoward(double %subnormal, fp128 %zero)
+ ret double %next
+}
+define double @nexttoward_not_marked_dead_on_subnormal() {
+; CHECK-LABEL: define double @nexttoward_not_marked_dead_on_subnormal() {
+; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, fp128 0xL00000000000000003FFF000000000000)
+; CHECK-NEXT: ret double 9.881310e-324
+;
+ %subnormal = load double, double* @dbl_pos_min_subnormal
+ %target = fpext double 1.0 to fp128
+ %next = call double @nexttoward(double %subnormal, fp128 %target)
+ ret double %next
+}
+
+; ==================
+; nexttowardf tests
+; ==================
+
+define float @nexttowardf_can_constant_fold_up_direction() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_up_direction() {
+; CHECK-NEXT: ret float 0x3FF0000020000000
+;
+ %arg = fpext float 2.0 to fp128
+ %next = call float @nexttowardf(float noundef 1.0, fp128 %arg)
+ ret float %next
+}
+define float @nexttowardf_can_constant_fold_down_direction() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_down_direction() {
+; CHECK-NEXT: ret float 0x3FEFFFFFE0000000
+;
+ %arg = fpext float 0.0 to fp128
+ %next = call float @nexttowardf(float noundef 1.0, fp128 %arg)
+ ret float %next
+}
+define float @nexttowardf_can_constant_fold_equal_args() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_equal_args() {
+; CHECK-NEXT: ret float 1.000000e+00
+;
+ %arg = fpext float 1.0 to fp128
+ %next = call float @nexttowardf(float 1.0, fp128 %arg)
+ ret float %next
+}
+define float @nexttowardf_can_constant_fold_with_nan_arg() {
+; CHECK-LABEL: define float @nexttowardf_can_constant_fold_with_nan_arg() {
+; CHECK-NEXT: ret float 0x7FF8000000000000
+;
+ %nan = load float, float* @flt_nan
+ %ext_nan = fpext float %nan to fp128
+ %next = call float @nexttowardf(float 1.0, fp128 %ext_nan)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_pos_overflow () {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_pos_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x47EFFFFFE0000000, fp128 0xL00000000000000007FFF000000000000)
+; CHECK-NEXT: ret float 0x7FF0000000000000
+;
+ %pos_max = load float, float* @flt_pos_max
+ %pos_inf = load float, float* @flt_pos_infinity
+ %ext_pos_inf = fpext float %pos_inf to fp128
+ %next = call float @nexttowardf(float %pos_max, fp128 %ext_pos_inf)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_neg_overflow() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_neg_overflow() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0xC7EFFFFFE0000000, fp128 0xL0000000000000000FFFF000000000000)
+; CHECK-NEXT: ret float 0xFFF0000000000000
+;
+ %neg_max = load float, float* @flt_neg_max
+ %neg_inf = load float, float* @flt_neg_infinity
+ %ext_neg_inf = fpext float %neg_inf to fp128
+ %next = call float @nexttowardf(float %neg_max, fp128 %ext_neg_inf)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_zero_from_above() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_zero_from_above() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret float 0.000000e+00
+;
+ %min_subnormal = load float, float* @flt_pos_min_subnormal
+ %zero = fpext float 0.0 to fp128
+ %next = call float @nexttowardf(float %min_subnormal, fp128 %zero)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_zero_from_below() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_zero_from_below() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0xB6A0000000000000, fp128 0xL00000000000000000000000000000000)
+; CHECK-NEXT: ret float -0.000000e+00
+;
+ %min_subnormal = load float, float* @flt_neg_min_subnormal
+ %zero = fpext float 0.0 to fp128
+ %next = call float @nexttowardf(float %min_subnormal, fp128 %zero)
+ ret float %next
+}
+define float @nexttowardf_not_marked_dead_on_subnormal() {
+; CHECK-LABEL: define float @nexttowardf_not_marked_dead_on_subnormal() {
+; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, fp128 0xL00000000000000003FFF000000000000)
+; CHECK-NEXT: ret float 0x36B0000000000000
+;
+ %subnormal = load float, float* @flt_pos_min_subnormal
+ %target = fpext float 1.0 to fp128
+ %next = call float @nexttowardf(float %subnormal, fp128 %target)
+ ret float %next
+}
diff --git a/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll
new file mode 100644
index 0000000000000..30da81b1bd01f
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll
@@ -0,0 +1,187 @@
+; 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 @nexttoward(double noundef, ppc_fp128 noundef) #0
+declare float @nexttowardf(float noundef, ppc_fp128 noundef) #0
+
+attribut...
[truncated]
|
| APFloat PromotedOp1V = Op1V.getPromoted(APFloat::IEEEquad()); | ||
| APFloat PromotedOp2V = Op2V.getPromoted(APFloat::IEEEquad()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you promoting these values here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I should have added a comment here.
The problem is that nexttoward has different types for their first and second payloads, so we can't compare the operands without them having the same semantics. I initially tried promoting the first operand to the type of the second operand, because I believe long double should be able to hold all of the values of float and double. However, I ran into an assertion error on PowerPC stating that semIEEEDouble isn't representably by semPPCDoubleDoubleLegacy, presumably because the minimum exponent of the former is less than the minimum exponent of the latter.
I might have used the APIs incorrectly, but I decided to keep things simple and convert everything to IEEEQuad because its semantics is presumably large enough for me to compare the two operands no matter the floating point type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PPC long double is a double + double representation, which means every double is trivially representable in it. PPC long double is not representable in an fp128, however, since there can be effectively a pretty wide mantissa, much longer than the mantissa of a fp128.
(The problems of the PPC long double type are one of the reasons long double optimization is somewhat disfavored).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, okay. It sounds like the getPromoted function I added is broken then, since it refuses to do a legal promotion of double to long double on PPC, and would permit an invalid "promotion" from ppc_fp128 to fp128.
I'm not immediately sure if there is an equivalent function that would work when comparing IEEEFloat and DoubleAPFloat layouts, since if I understand correctly isRepresentablyBy can only compare IEEEFloat layouts. It's not something I have the knowledge to work with yet, and I probably wouldn't want to touch that in this MR even if I did.
I think it should be sufficient then to just use the APFloat::convert API to upgrade the first argument's semantics to the second argument's, which is always safe. It sounds like I never needed to touch the ADT module, sorry about that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should also add, C and C++ both guarantee that the set of values for long double is a (not necessarily strict) superset of the set of values representable for double.
| return ConstantFP::get(Ty->getContext(), | ||
| APFloat::getNaN(Ty->getFltSemantics())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we're constant folding NaNs, we should be making some effort to do payload propagation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this should be done. I also added lit tests to assert that we propagate NaN payloads.
I decided to not propagate payloads if the NaN is the second argument to nexttoward, since the payload could be cut off if we convert to the return type. Hopefully that seems reasonable. Propagating the payload of the second argument in the case of nextafter should be safe.
| APFloat PromotedOp0 = Op0.getPromoted(APFloat::IEEEquad()); | ||
| APFloat PromotedOp1 = Op1.getPromoted(APFloat::IEEEquad()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, why are you promoting these values here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same reason as mentioned in this reply.
8f6fa1b to
678ba1e
Compare
kuhar
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ADT changes look fine % a nit.
I haven't reviewed the other code and test cases -- please wait for a second approval before landing.
| return Name == "nearbyint" || Name == "nearbyintf" || Name == "nextafter" || | ||
| Name == "nextafterf" || Name == "nexttoward" || | ||
| Name == "nexttowardf"; |
There was a problem hiding this comment.
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
| %subnormal = load double, double* @dbl_pos_min_subnormal | ||
| %next = call double @nextafter(double %subnormal, double 1.0) readnone | ||
| ret double %next | ||
| } |
There was a problem hiding this comment.
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?
|
Taking this MR into draft to do a small rework of the implementation. After talking with Joshua Cranmer, it seems like I never needed to touch I'm guessing that only people from the Let me know if you would prefer that I close this MR and start another one with the simpler implementation. I think it would make sure you two don't get emails about changes you don't care about, and would give me a chance to clean up the messy git history. |
|
Closing this for a simpler PR I'll open after I get back from vacation early next week. It might also be smart to wait to see what discussion comes out of #167475, since it seems like there is some interest in not constant folding NaNs at all. |
Fixes issue #74368.
This patch enables us to constant fold
nextafterandnexttowardas long as we know thaterrnowon't be written. In the latter case, we should keep the function call so the programmer can observe the side effect.Apologies for the large patch, the majority of it are
littests. I wasn't sure how to cut down on the repetitiveness here, let me know if you have any better ideas. I got the idea for reusing the constants across.llfiles from the LLVM Discord.