-
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
Changes from all commits
0065b07
e94017f
a0029be
1de09dd
678ba1e
8b3a10c
70a9760
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW, all of the surrounding code can be simplified with |
||
| case 'p': | ||
| return Name == "pow" || Name == "powf"; | ||
| case 'r': | ||
|
|
@@ -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); | ||
sivakusayan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| return nullptr; | ||
|
|
@@ -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; | ||
| } | ||
|
|
||
| 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 | ||
| } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit confused why adding |
||
|
|
||
| ; ================== | ||
| ; 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 | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.