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..0253606c4eedb 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,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; @@ -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; } 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..5ffcaef5be9c0 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll @@ -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 +} + +; ================== +; 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 +} 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..dedf3e76a788e --- /dev/null +++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll @@ -0,0 +1,211 @@ +; 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_constant_fold_propagates_nan_payload() { +; CHECK-LABEL: define double @nexttoward_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 + %dummy_arg = fpext double 1.0 to fp128 + %next = call double @nexttoward(double %nan_with_payload, fp128 %dummy_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_constant_fold_propagates_nan_payload() { +; CHECK-LABEL: define float @nexttowardf_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 + %dummy_arg = fpext float 1.0 to fp128 + %next = call float @nexttowardf(float %nan_with_payload, fp128 %dummy_arg) + 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..718e3333cefee --- /dev/null +++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll @@ -0,0 +1,211 @@ +; 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 + +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 ppc_fp128 + %next = call double @nexttoward(double noundef 1.0, ppc_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 ppc_fp128 + %next = call double @nexttoward(double noundef 1.0, ppc_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 ppc_fp128 + %next = call double @nexttoward(double 1.0, ppc_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 ppc_fp128 + %next = call double @nexttoward(double 1.0, ppc_fp128 %arg) + ret double %next +} +define double @nexttoward_constant_fold_propagates_nan_payload() { +; CHECK-LABEL: define double @nexttoward_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 + %dummy_arg = fpext double 1.0 to ppc_fp128 + %next = call double @nexttoward(double %nan_with_payload, ppc_fp128 %dummy_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, ppc_fp128 0xM7FF00000000000000000000000000000) +; 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 ppc_fp128 + %next = call double @nexttoward(double %pos_max, ppc_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, ppc_fp128 0xMFFF00000000000000000000000000000) +; 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 ppc_fp128 + %next = call double @nexttoward(double %neg_max, ppc_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, ppc_fp128 0xM00000000000000000000000000000000) +; CHECK-NEXT: ret double 0.000000e+00 +; + %subnormal = load double, double* @dbl_pos_min_subnormal + %zero = fpext double 0.0 to ppc_fp128 + %next = call double @nexttoward(double %subnormal, ppc_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, ppc_fp128 0xM00000000000000000000000000000000) +; CHECK-NEXT: ret double -0.000000e+00 +; + %subnormal = load double, double* @dbl_neg_min_subnormal + %zero = fpext double 0.0 to ppc_fp128 + %next = call double @nexttoward(double %subnormal, ppc_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, ppc_fp128 0xM3FF00000000000000000000000000000) +; CHECK-NEXT: ret double 9.881310e-324 +; + %subnormal = load double, double* @dbl_pos_min_subnormal + %target = fpext double 1.0 to ppc_fp128 + %next = call double @nexttoward(double %subnormal, ppc_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 ppc_fp128 + %next = call float @nexttowardf(float noundef 1.0, ppc_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 ppc_fp128 + %next = call float @nexttowardf(float noundef 1.0, ppc_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 ppc_fp128 + %next = call float @nexttowardf(float 1.0, ppc_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 ppc_fp128 + %next = call float @nexttowardf(float 1.0, ppc_fp128 %ext_nan) + ret float %next +} +define float @nexttowardf_constant_fold_propagates_nan_payload() { +; CHECK-LABEL: define float @nexttowardf_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 + %dummy_arg = fpext float 1.0 to ppc_fp128 + %next = call float @nexttowardf(float %nan_with_payload, ppc_fp128 %dummy_arg) + 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, ppc_fp128 0xM7FF00000000000000000000000000000) +; 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 ppc_fp128 + %next = call float @nexttowardf(float %pos_max, ppc_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, ppc_fp128 0xMFFF00000000000000000000000000000) +; 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 ppc_fp128 + %next = call float @nexttowardf(float %neg_max, ppc_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, ppc_fp128 0xM00000000000000000000000000000000) +; CHECK-NEXT: ret float 0.000000e+00 +; + %min_subnormal = load float, float* @flt_pos_min_subnormal + %zero = fpext float 0.0 to ppc_fp128 + %next = call float @nexttowardf(float %min_subnormal, ppc_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, ppc_fp128 0xM00000000000000000000000000000000) +; CHECK-NEXT: ret float -0.000000e+00 +; + %min_subnormal = load float, float* @flt_neg_min_subnormal + %zero = fpext float 0.0 to ppc_fp128 + %next = call float @nexttowardf(float %min_subnormal, ppc_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, ppc_fp128 0xM3FF00000000000000000000000000000) +; CHECK-NEXT: ret float 0x36B0000000000000 +; + %subnormal = load float, float* @flt_pos_min_subnormal + %target = fpext float 1.0 to ppc_fp128 + %next = call float @nexttowardf(float %subnormal, ppc_fp128 %target) + ret float %next +} diff --git a/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-x86-fp80.ll b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-x86-fp80.ll new file mode 100644 index 0000000000000..590d0655f4009 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-x86-fp80.ll @@ -0,0 +1,211 @@ +; 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, x86_fp80 noundef) #0 +declare float @nexttowardf(float noundef, x86_fp80 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 x86_fp80 + %next = call double @nexttoward(double noundef 1.0, x86_fp80 %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 x86_fp80 + %next = call double @nexttoward(double noundef 1.0, x86_fp80 %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 x86_fp80 + %next = call double @nexttoward(double 1.0, x86_fp80 %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 x86_fp80 + %next = call double @nexttoward(double 1.0, x86_fp80 %arg) + ret double %next +} +define double @nexttoward_constant_fold_propagates_nan_payload() { +; CHECK-LABEL: define double @nexttoward_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 + %dummy_arg = fpext double 1.0 to x86_fp80 + %next = call double @nexttoward(double %nan_with_payload, x86_fp80 %dummy_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, x86_fp80 0xK7FFF8000000000000000) +; 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 x86_fp80 + %next = call double @nexttoward(double %pos_max, x86_fp80 %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, x86_fp80 0xKFFFF8000000000000000) +; 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 x86_fp80 + %next = call double @nexttoward(double %neg_max, x86_fp80 %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, x86_fp80 0xK00000000000000000000) +; CHECK-NEXT: ret double 0.000000e+00 +; + %subnormal = load double, double* @dbl_pos_min_subnormal + %zero = fpext double 0.0 to x86_fp80 + %next = call double @nexttoward(double %subnormal, x86_fp80 %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, x86_fp80 0xK00000000000000000000) +; CHECK-NEXT: ret double -0.000000e+00 +; + %subnormal = load double, double* @dbl_neg_min_subnormal + %zero = fpext double 0.0 to x86_fp80 + %next = call double @nexttoward(double %subnormal, x86_fp80 %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, x86_fp80 0xK3FFF8000000000000000) +; CHECK-NEXT: ret double 9.881310e-324 +; + %subnormal = load double, double* @dbl_pos_min_subnormal + %target = fpext double 1.0 to x86_fp80 + %next = call double @nexttoward(double %subnormal, x86_fp80 %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 x86_fp80 + %next = call float @nexttowardf(float noundef 1.0, x86_fp80 %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 x86_fp80 + %next = call float @nexttowardf(float noundef 1.0, x86_fp80 %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 x86_fp80 + %next = call float @nexttowardf(float 1.0, x86_fp80 %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 x86_fp80 + %next = call float @nexttowardf(float 1.0, x86_fp80 %ext_nan) + ret float %next +} +define float @nexttowardf_constant_fold_propagates_nan_payload() { +; CHECK-LABEL: define float @nexttowardf_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 + %dummy_arg = fpext float 1.0 to x86_fp80 + %next = call float @nexttowardf(float %nan_with_payload, x86_fp80 %dummy_arg) + 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, x86_fp80 0xK7FFF8000000000000000) +; 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 x86_fp80 + %next = call float @nexttowardf(float %pos_max, x86_fp80 %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, x86_fp80 0xKFFFF8000000000000000) +; 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 x86_fp80 + %next = call float @nexttowardf(float %neg_max, x86_fp80 %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, x86_fp80 0xK00000000000000000000) +; CHECK-NEXT: ret float 0.000000e+00 +; + %min_subnormal = load float, float* @flt_pos_min_subnormal + %zero = fpext float 0.0 to x86_fp80 + %next = call float @nexttowardf(float %min_subnormal, x86_fp80 %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, x86_fp80 0xK00000000000000000000) +; CHECK-NEXT: ret float -0.000000e+00 +; + %min_subnormal = load float, float* @flt_neg_min_subnormal + %zero = fpext float 0.0 to x86_fp80 + %next = call float @nexttowardf(float %min_subnormal, x86_fp80 %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, x86_fp80 0xK3FFF8000000000000000) +; CHECK-NEXT: ret float 0x36B0000000000000 +; + %subnormal = load float, float* @flt_pos_min_subnormal + %target = fpext float 1.0 to x86_fp80 + %next = call float @nexttowardf(float %subnormal, x86_fp80 %target) + ret float %next +} diff --git a/llvm/test/Transforms/InstCombine/floating-point-constants.ll b/llvm/test/Transforms/InstCombine/floating-point-constants.ll new file mode 100644 index 0000000000000..5d135b0aaebaa --- /dev/null +++ b/llvm/test/Transforms/InstCombine/floating-point-constants.ll @@ -0,0 +1,48 @@ +; RUN: true +; Constants for edge case float testing. + +; Generated with: +; clang -S -emit-llvm floating-point-constants.c +; +; // floating-point-constants.c +; #include +; #include +; +; const float flt_nan = NAN; +; const float flt_pos_min_subnormal = FLT_TRUE_MIN; +; const float flt_pos_min_normal = FLT_MIN; +; const float flt_pos_max = FLT_MAX; +; const float flt_pos_infinity = INFINITY; +; const float flt_neg_min_subnormal = -FLT_TRUE_MIN; +; const float flt_neg_min_normal = -FLT_MIN; +; const float flt_neg_max = -FLT_MAX; +; const float flt_neg_infinity = -INFINITY; +; +; const double dbl_nan = NAN; +; const double dbl_pos_min_subnormal = DBL_TRUE_MIN; +; const double dbl_pos_min_normal = DBL_MIN; +; const double dbl_pos_max = DBL_MAX; +; const double dbl_pos_infinity = INFINITY; +; const double dbl_neg_min_subnormal = -DBL_TRUE_MIN; +; const double dbl_neg_min_normal = -DBL_MIN; +; const double dbl_neg_max = -DBL_MAX; +; const double dbl_neg_infinity = -INFINITY; + +@flt_nan = dso_local constant float 0x7FF8000000000000, align 4 +@flt_pos_min_subnormal = dso_local constant float 0x36A0000000000000, align 4 +@flt_pos_min_normal = dso_local constant float 0x3810000000000000, align 4 +@flt_pos_max = dso_local constant float 0x47EFFFFFE0000000, align 4 +@flt_pos_infinity = dso_local constant float 0x7FF0000000000000, align 4 +@flt_neg_min_subnormal = dso_local constant float 0xB6A0000000000000, align 4 +@flt_neg_min_normal = dso_local constant float 0xB810000000000000, align 4 +@flt_neg_max = dso_local constant float 0xC7EFFFFFE0000000, align 4 +@flt_neg_infinity = dso_local constant float 0xFFF0000000000000, align 4 +@dbl_nan = dso_local constant double 0x7FF8000000000000, align 8 +@dbl_pos_min_subnormal = dso_local constant double 4.940660e-324, align 8 +@dbl_pos_min_normal = dso_local constant double 0x10000000000000, align 8 +@dbl_pos_max = dso_local constant double 0x7FEFFFFFFFFFFFFF, align 8 +@dbl_pos_infinity = dso_local constant double 0x7FF0000000000000, align 8 +@dbl_neg_min_subnormal = dso_local constant double -4.940660e-324, align 8 +@dbl_neg_min_normal = dso_local constant double 0x8010000000000000, align 8 +@dbl_neg_max = dso_local constant double 0xFFEFFFFFFFFFFFFF, align 8 +@dbl_neg_infinity = dso_local constant double 0xFFF0000000000000, align 8 diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp index 99cc38b6b422b..a6fe35e66684c 100644 --- a/llvm/unittests/ADT/APFloatTest.cpp +++ b/llvm/unittests/ADT/APFloatTest.cpp @@ -10182,4 +10182,30 @@ TEST(APFloatTest, FrexpQuietSNaN) { EXPECT_FALSE(Result.isSignaling()); } +TEST(APFloatTest, getPromoted) { + // Strengthening promotions are allowed. + APFloat ValidPromotionTest(1.0f); + APFloat DoubleVal = ValidPromotionTest.getPromoted(APFloat::IEEEdouble()); + + // Trivial promotions to the same semantics are allowed. + DoubleVal.getPromoted(DoubleVal.getSemantics()); + + // Promotions have the expected semantics and preserve the initial value. + EXPECT_EQ(&DoubleVal.getSemantics(), &APFloat::IEEEdouble()); + EXPECT_EQ(1.0, DoubleVal.convertToDouble()); + +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + // All invalid promotions are swiftly killed. + APFloat InvalidPromotionTest = + ValidPromotionTest.getPromoted(APFloat::IEEEquad()); + EXPECT_DEATH(InvalidPromotionTest.getPromoted(APFloat::IEEEdouble()), + "Target semantics will lose information"); + EXPECT_DEATH(InvalidPromotionTest.getPromoted(APFloat::x87DoubleExtended()), + "Target semantics will lose information"); + EXPECT_DEATH( + InvalidPromotionTest.getPromoted(APFloat::PPCDoubleDoubleLegacy()), + "Target semantics will lose information"); +#endif +} + } // namespace