diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index 4bece85d3cfbf..743ce92a5c1ea 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -2008,7 +2008,9 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) { Name == "log10f" || Name == "logb" || Name == "logbf" || Name == "log1p" || Name == "log1pf"; case 'n': - return Name == "nearbyint" || Name == "nearbyintf"; + return Name == "nearbyint" || Name == "nearbyintf" || Name == "nextafter" || + Name == "nextafterf" || Name == "nexttoward" || + Name == "nexttowardf"; case 'p': return Name == "pow" || Name == "powf"; case 'r': @@ -3174,6 +3176,57 @@ static Constant *evaluateCompare(const APFloat &Op1, const APFloat &Op2, return nullptr; } +/// Returns the first NaN in the operand list if it exists, preserving the NaN +/// payload if possible. Returns nullptr if no NaNs are in the list. +static Constant *TryConstantFoldNaN(ArrayRef Operands, + const Type *RetTy) { + assert(RetTy != nullptr); + for (const APFloat &Op : Operands) { + if (Op.isNaN()) { + bool Unused; + APFloat Ret(Op); + Ret.convert(RetTy->getFltSemantics(), detail::rmNearestTiesToEven, + &Unused); + return ConstantFP::get(RetTy->getContext(), Ret); + } + } + return nullptr; +} + +static Constant *ConstantFoldNextToward(const APFloat &Op0, const APFloat &Op1, + const Type *RetTy) { + assert(RetTy != nullptr); + if (Constant *RetNaN = TryConstantFoldNaN({Op0, Op1}, RetTy)) + return RetNaN; + + // Recall that the second argument of nexttoward is always a long double, + // so we may need to promote the first argument for comparisons to be valid. + bool LosesInfo; + APFloat PromotedOp0(Op0); + PromotedOp0.convert(Op1.getSemantics(), detail::rmNearestTiesToEven, + &LosesInfo); + assert(!LosesInfo && "Unexpected lossy promotion"); + const APFloat::cmpResult Result = PromotedOp0.compare(Op1); + assert(Result != APFloat::cmpUnordered && "Unexpected NaN"); + + // When equal, the standard says we must return the second argument. + // This allows nice behavior such as nexttoward(0.0, -0.0) = -0.0 and + // nexttoward(-0.0, 0.0) = 0.0 + if (Result == detail::cmpEqual) { + APFloat Ret(Op1); + Ret.convert(RetTy->getFltSemantics(), detail::rmNearestTiesToEven, + &LosesInfo); + return ConstantFP::get(RetTy->getContext(), Ret); + } + + APFloat Next(Op0); + Next.next(/*nextDown=*/Result == APFloat::cmpGreaterThan); + const bool DidOverflow = !Op0.isInfinity() && Next.isInfinity(); + if (Next.isZero() || Next.isDenormal() || DidOverflow) + return nullptr; + return ConstantFP::get(RetTy->getContext(), Next); +} + static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty, ArrayRef Operands, const TargetLibraryInfo *TLI) { @@ -3233,6 +3286,13 @@ 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)) { + return ConstantFoldNextToward(Op1V, Op2V, Ty); + } } return nullptr; @@ -4685,6 +4745,14 @@ 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: { + return ConstantFoldNextToward(Op0, Op1, F->getReturnType()) != nullptr; + } 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..63d0821279f7d --- /dev/null +++ b/llvm/test/Transforms/InstCombine/constant-fold-nextafter.ll @@ -0,0 +1,255 @@ +; 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 + +define double @nextafter_up_direction() { +; CHECK-LABEL: define double @nextafter_up_direction() { +; CHECK-NEXT: ret double 0x3FF0000000000001 +; + %next = call double @nextafter(double 1.0, double 2.0) + ret double %next +} + +define float @nextafterf_up_direction() { +; CHECK-LABEL: define float @nextafterf_up_direction() { +; CHECK-NEXT: ret float 0x3FF0000020000000 +; + %next = call float @nextafterf(float 1.0, float 2.0) + ret float %next +} + +define double @nextafter_down_direction() { +; CHECK-LABEL: define double @nextafter_down_direction() { +; CHECK-NEXT: ret double 0x3FEFFFFFFFFFFFFF +; + %next = call double @nextafter(double 1.0, double 0.0) + ret double %next +} + +define float @nextafterf_down_direction() { +; CHECK-LABEL: define float @nextafterf_down_direction() { +; CHECK-NEXT: ret float 0x3FEFFFFFE0000000 +; + %next = call float @nextafterf(float 1.0, float 0.0) + ret float %next +} + +define double @nextafter_equal_args() { +; CHECK-LABEL: define double @nextafter_equal_args() { +; CHECK-NEXT: ret double 1.000000e+00 +; + %next = call double @nextafter(double 1.0, double 1.0) + ret double %next +} + +define float @nextafterf_equal_args() { +; CHECK-LABEL: define float @nextafterf_equal_args() { +; CHECK-NEXT: ret float 1.000000e+00 +; + %next = call float @nextafterf(float 1.0, float 1.0) + ret float %next +} + +define double @nextafter_pos_zero_neg_zero() { +; CHECK-LABEL: define double @nextafter_pos_zero_neg_zero() { +; CHECK-NEXT: ret double -0.000000e+00 +; + %next = call double @nextafter(double 0.0, double -0.0) + ret double %next +} + +define float @nextafterf_pos_zero_neg_zero() { +; CHECK-LABEL: define float @nextafterf_pos_zero_neg_zero() { +; CHECK-NEXT: ret float -0.000000e+00 +; + %next = call float @nextafterf(float 0.0, float -0.0) + ret float %next +} + +define double @nextafter_neg_zero_pos_zero() { +; CHECK-LABEL: define double @nextafter_neg_zero_pos_zero() { +; CHECK-NEXT: ret double 0.000000e+00 +; + %next = call double @nextafter(double -0.0, double 0.0) + ret double %next +} + +define float @nextafterf_neg_zero_pos_zero() { +; CHECK-LABEL: define float @nextafterf_neg_zero_pos_zero() { +; CHECK-NEXT: ret float 0.000000e+00 +; + %next = call float @nextafterf(float -0.0, float 0.0) + ret float %next +} + +define double @nextafter_nan_with_payload() { +; CHECK-LABEL: define double @nextafter_nan_with_payload() { +; CHECK-NEXT: ret double 0x7FF8000000000001 +; + %tmp1 = load i64, ptr @dbl_nan + %tmp2 = or i64 %tmp1, 1 + %nan_with_payload = bitcast i64 %tmp2 to double + %next = call double @nextafter(double %nan_with_payload, double 1.0) + ret double %next + +} + +define float @nextafterf_nan_with_payload() { +; CHECK-LABEL: define float @nextafterf_nan_with_payload() { +; CHECK-NEXT: ret float 0x7FF8000020000000 +; + %tmp1 = load i32, ptr @flt_nan + %tmp2 = or i32 %tmp1, 1 + %nan_with_payload = bitcast i32 %tmp2 to float + %next = call float @nextafterf(float %nan_with_payload, float 1.0) + ret float %next +} + +define double @nextafter_pos_overflow () { +; CHECK-LABEL: define double @nextafter_pos_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 0x7FEFFFFFFFFFFFFF, double 0x7FF0000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %arg1 = load double, ptr @dbl_pos_max + %arg2 = load double, ptr @dbl_pos_infinity + %next = call double @nextafter(double %arg1, double %arg2) + ret double %next +} + +define float @nextafterf_pos_overflow() { +; CHECK-LABEL: define float @nextafterf_pos_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0x47EFFFFFE0000000, float 0x7FF0000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %arg1 = load float, ptr @flt_pos_max + %arg2 = load float, ptr @flt_pos_infinity + %next = call float @nextafterf(float %arg1, float %arg2) + ret float %next +} + +define double @nextafter_neg_overflow() { +; CHECK-LABEL: define double @nextafter_neg_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 0xFFEFFFFFFFFFFFFF, double 0xFFF0000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %arg1 = load double, ptr @dbl_neg_max + %arg2 = load double, ptr @dbl_neg_infinity + %next = call double @nextafter(double %arg1, double %arg2) + ret double %next +} + +define float @nextafterf_neg_overflow() { +; CHECK-LABEL: define float @nextafterf_neg_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0xC7EFFFFFE0000000, float 0xFFF0000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %arg1 = load float, ptr @flt_neg_max + %arg2 = load float, ptr @flt_neg_infinity + %next = call float @nextafterf(float %arg1, float %arg2) + ret float %next +} + +define double @nextafter_zero_from_above() { +; CHECK-LABEL: define double @nextafter_zero_from_above() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 4.940660e-324, double 0.000000e+00) +; CHECK-NEXT: ret double [[NEXT]] +; + %arg = load double, ptr @dbl_pos_min_subnormal + %next = call double @nextafter(double %arg, double 0.0) + ret double %next +} + +define float @nextafterf_zero_from_above() { +; CHECK-LABEL: define float @nextafterf_zero_from_above() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0x36A0000000000000, float 0.000000e+00) +; CHECK-NEXT: ret float [[NEXT]] +; + %arg = load float, ptr @flt_pos_min_subnormal + %next = call float @nextafterf(float %arg, float 0.0) + ret float %next +} + +define double @nextafter_zero_from_below() { +; CHECK-LABEL: define double @nextafter_zero_from_below() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double -4.940660e-324, double 0.000000e+00) +; CHECK-NEXT: ret double [[NEXT]] +; + %arg = load double, ptr @dbl_neg_min_subnormal + %next = call double @nextafter(double %arg, double 0.0) + ret double %next +} + +define float @nextafterf_zero_from_below() { +; CHECK-LABEL: define float @nextafterf_zero_from_below() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0xB6A0000000000000, float 0.000000e+00) +; CHECK-NEXT: ret float [[NEXT]] +; + %arg = load float, ptr @flt_neg_min_subnormal + %next = call float @nextafterf(float %arg, float 0.0) + ret float %next +} + +define double @nextafter_subnormal() { +; CHECK-LABEL: define double @nextafter_subnormal() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 4.940660e-324, double 0x7FF0000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %subnormal = load double, ptr @dbl_pos_min_subnormal + %infinity = load double, ptr @dbl_pos_infinity + %next = call double @nextafter(double %subnormal, double %infinity) + ret double %next +} + +define float @nextafterf_subnormal() { +; CHECK-LABEL: define float @nextafterf_subnormal() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0x36A0000000000000, float 0x7FF0000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %subnormal = load float, ptr @flt_pos_min_subnormal + %infinity = load float, ptr @flt_pos_infinity + %next = call float @nextafterf(float %subnormal, float %infinity) + ret float %next +} + +define double @nextafter_poison() { +; CHECK-LABEL: define double @nextafter_poison() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double poison, double 1.000000e+00) +; CHECK-NEXT: ret double [[NEXT]] +; + %next = call double @nextafter(double poison, double 1.0) + ret double %next +} + +define double @nextafterf_poison() { +; CHECK-LABEL: define double @nextafterf_poison() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafterf(float poison, float 1.000000e+00) +; CHECK-NEXT: ret double [[NEXT]] +; + %next = call double @nextafterf(float poison, float 1.0) + ret double %next +} + +define double @nextafter_subnormal_readnone() { +; CHECK-LABEL: define double @nextafter_subnormal_readnone() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nextafter(double 4.940660e-324, double 0x7FF0000000000000) #[[ATTR1:[0-9]+]] +; CHECK-NEXT: ret double [[NEXT]] +; + %subnormal = load double, ptr @dbl_pos_min_subnormal + %infinity = load double, ptr @dbl_pos_infinity + %next = call double @nextafter(double %subnormal, double %infinity) readnone + ret double %next +} + +define float @nextafterf_subnormal_readnone() { +; CHECK-LABEL: define float @nextafterf_subnormal_readnone() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nextafterf(float 0x36A0000000000000, float 0x7FF0000000000000) #[[ATTR1]] +; CHECK-NEXT: ret float [[NEXT]] +; + %subnormal = load float, ptr @flt_pos_min_subnormal + %infinity = load float, ptr @flt_pos_infinity + %next = call float @nextafterf(float %subnormal, float %infinity) readnone + ret float %next +} + +declare double @nextafter(double, double) #0 +declare float @nextafterf(float, float) #0 +attributes #0 = { willreturn memory(errnomem: write) } 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..c387563c68f9c --- /dev/null +++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-fp128.ll @@ -0,0 +1,276 @@ +; 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 + +define double @nexttoward_up_direction() { +; CHECK-LABEL: define double @nexttoward_up_direction() { +; CHECK-NEXT: ret double 0x3FF0000000000001 +; + %arg = fpext double 2.0 to fp128 + %next = call double @nexttoward(double 1.0, fp128 %arg) + ret double %next +} + +define float @nexttowardf_up_direction() { +; CHECK-LABEL: define float @nexttowardf_up_direction() { +; CHECK-NEXT: ret float 0x3FF0000020000000 +; + %arg = fpext float 2.0 to fp128 + %next = call float @nexttowardf(float 1.0, fp128 %arg) + ret float %next +} + +define double @nexttoward_down_direction() { +; CHECK-LABEL: define double @nexttoward_down_direction() { +; CHECK-NEXT: ret double 0x3FEFFFFFFFFFFFFF +; + %arg = fpext double 0.0 to fp128 + %next = call double @nexttoward(double 1.0, fp128 %arg) + ret double %next +} + +define float @nexttowardf_down_direction() { +; CHECK-LABEL: define float @nexttowardf_down_direction() { +; CHECK-NEXT: ret float 0x3FEFFFFFE0000000 +; + %arg = fpext float 0.0 to fp128 + %next = call float @nexttowardf(float 1.0, fp128 %arg) + ret float %next +} + +define double @nexttoward_equal_args() { +; CHECK-LABEL: define double @nexttoward_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 float @nexttowardf_equal_args() { +; CHECK-LABEL: define float @nexttowardf_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 double @nexttoward_pos_zero_neg_zero() { +; CHECK-LABEL: define double @nexttoward_pos_zero_neg_zero() { +; CHECK-NEXT: ret double -0.000000e+00 +; + %neg_zero_ext = fpext double -0.0 to fp128 + %next = call double @nexttoward(double 0.0, fp128 %neg_zero_ext) + ret double %next +} + +define float @nexttowardf_pos_zero_neg_zero() { +; CHECK-LABEL: define float @nexttowardf_pos_zero_neg_zero() { +; CHECK-NEXT: ret float -0.000000e+00 +; + %neg_zero_ext = fpext float -0.0 to fp128 + %next = call float @nexttowardf(float 0.0, fp128 %neg_zero_ext) + ret float %next +} + +define double @nexttoward_neg_zero_pos_zero() { +; CHECK-LABEL: define double @nexttoward_neg_zero_pos_zero() { +; CHECK-NEXT: ret double 0.000000e+00 +; + %pos_zero_ext = fpext double 0.0 to fp128 + %next = call double @nexttoward(double -0.0, fp128 %pos_zero_ext) + ret double %next +} + +define float @nexttowardf_neg_zero_pos_zero() { +; CHECK-LABEL: define float @nexttowardf_neg_zero_pos_zero() { +; CHECK-NEXT: ret float 0.000000e+00 +; + %pos_zero_ext = fpext float 0.0 to fp128 + %next = call float @nexttowardf(float -0.0, fp128 %pos_zero_ext) + ret float %next +} + +define double @nexttoward_nan_with_payload() { +; CHECK-LABEL: define double @nexttoward_nan_with_payload() { +; CHECK-NEXT: ret double 0x7FF8000000000001 +; + %tmp1 = load i64, ptr @dbl_nan + %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 float @nexttowardf_nan_with_payload() { +; CHECK-LABEL: define float @nexttowardf_nan_with_payload() { +; CHECK-NEXT: ret float 0x7FF8000020000000 +; + %tmp1 = load i32, ptr @flt_nan + %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 double @nexttoward_pos_overflow() { +; CHECK-LABEL: define double @nexttoward_pos_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0x7FEFFFFFFFFFFFFF, fp128 0xL00000000000000007FFF000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %pos_max = load double, ptr @dbl_pos_max + %pos_inf = load double, ptr @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 float @nexttowardf_pos_overflow () { +; CHECK-LABEL: define float @nexttowardf_pos_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x47EFFFFFE0000000, fp128 0xL00000000000000007FFF000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %pos_max = load float, ptr @flt_pos_max + %pos_inf = load float, ptr @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 double @nexttoward_neg_overflow() { +; CHECK-LABEL: define double @nexttoward_neg_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0xFFEFFFFFFFFFFFFF, fp128 0xL0000000000000000FFFF000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %neg_max = load double, ptr @dbl_neg_max + %neg_inf = load double, ptr @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 float @nexttowardf_neg_overflow() { +; CHECK-LABEL: define float @nexttowardf_neg_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0xC7EFFFFFE0000000, fp128 0xL0000000000000000FFFF000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %neg_max = load float, ptr @flt_neg_max + %neg_inf = load float, ptr @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 double @nexttoward_zero_from_above() { +; CHECK-LABEL: define double @nexttoward_zero_from_above() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, fp128 0xL00000000000000000000000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %subnormal = load double, ptr @dbl_pos_min_subnormal + %zero = fpext double 0.0 to fp128 + %next = call double @nexttoward(double %subnormal, fp128 %zero) + ret double %next +} + +define float @nexttowardf_zero_from_above() { +; CHECK-LABEL: define float @nexttowardf_zero_from_above() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, fp128 0xL00000000000000000000000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %min_subnormal = load float, ptr @flt_pos_min_subnormal + %zero = fpext float 0.0 to fp128 + %next = call float @nexttowardf(float %min_subnormal, fp128 %zero) + ret float %next +} + +define double @nexttoward_zero_from_below() { +; CHECK-LABEL: define double @nexttoward_zero_from_below() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double -4.940660e-324, fp128 0xL00000000000000000000000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %subnormal = load double, ptr @dbl_neg_min_subnormal + %zero = fpext double 0.0 to fp128 + %next = call double @nexttoward(double %subnormal, fp128 %zero) + ret double %next +} + +define float @nexttowardf_zero_from_below() { +; CHECK-LABEL: define float @nexttowardf_zero_from_below() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0xB6A0000000000000, fp128 0xL00000000000000000000000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %min_subnormal = load float, ptr @flt_neg_min_subnormal + %zero = fpext float 0.0 to fp128 + %next = call float @nexttowardf(float %min_subnormal, fp128 %zero) + ret float %next +} + +define double @nexttoward_subnormal() { +; CHECK-LABEL: define double @nexttoward_subnormal() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, fp128 0xL00000000000000003FFF000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %subnormal = load double, ptr @dbl_pos_min_subnormal + %target = fpext double 1.0 to fp128 + %next = call double @nexttoward(double %subnormal, fp128 %target) + ret double %next +} + +define float @nexttowardf_subnormal() { +; CHECK-LABEL: define float @nexttowardf_subnormal() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, fp128 0xL00000000000000003FFF000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %subnormal = load float, ptr @flt_pos_min_subnormal + %target = fpext float 1.0 to fp128 + %next = call float @nexttowardf(float %subnormal, fp128 %target) + ret float %next +} + +define double @nexttoward_poison() { +; CHECK-LABEL: define double @nexttoward_poison() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double poison, fp128 0xL00000000000000003FFF000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %dummy_arg = fpext double 1.0 to fp128 + %next = call double @nexttoward(double poison, fp128 %dummy_arg) + ret double %next +} + +define float @nexttowardf_poison() { +; CHECK-LABEL: define float @nexttowardf_poison() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float poison, fp128 0xL00000000000000003FFF000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %dummy_arg = fpext double 1.0 to fp128 + %next = call float @nexttowardf(float poison, fp128 %dummy_arg) + ret float %next +} + +define double @nexttoward_subnormal_readnone() { +; CHECK-LABEL: define double @nexttoward_subnormal_readnone() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, fp128 0xL00000000000000003FFF000000000000) #[[ATTR1:[0-9]+]] +; CHECK-NEXT: ret double [[NEXT]] +; + %subnormal = load double, ptr @dbl_pos_min_subnormal + %target = fpext double 1.0 to fp128 + %next = call double @nexttoward(double %subnormal, fp128 %target) readnone + ret double %next +} + +define float @nexttowardf_subnormal_readnone() { +; CHECK-LABEL: define float @nexttowardf_subnormal_readnone() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, fp128 0xL00000000000000003FFF000000000000) #[[ATTR1]] +; CHECK-NEXT: ret float [[NEXT]] +; + %subnormal = load float, ptr @flt_pos_min_subnormal + %target = fpext float 1.0 to fp128 + %next = call float @nexttowardf(float %subnormal, fp128 %target) readnone + ret float %next +} + +declare double @nexttoward(double, fp128) #0 +declare float @nexttowardf(float, fp128) #0 +attributes #0 = { willreturn memory(errnomem: write) } 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..f029adfcf5d30 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-ppc-fp128.ll @@ -0,0 +1,112 @@ +; 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 + +define double @nexttoward_up_direction() { +; CHECK-LABEL: define double @nexttoward_up_direction() { +; CHECK-NEXT: ret double 0x3FF0000000000001 +; + %arg = fpext double 2.0 to ppc_fp128 + %next = call double @nexttoward(double 1.0, ppc_fp128 %arg) + ret double %next +} + +define float @nexttowardf_up_direction() { +; CHECK-LABEL: define float @nexttowardf_up_direction() { +; CHECK-NEXT: ret float 0x3FF0000020000000 +; + %arg = fpext float 2.0 to ppc_fp128 + %next = call float @nexttowardf(float 1.0, ppc_fp128 %arg) + ret float %next +} + +define double @nexttoward_down_direction() { +; CHECK-LABEL: define double @nexttoward_down_direction() { +; CHECK-NEXT: ret double 0x3FEFFFFFFFFFFFFF +; + %arg = fpext double 0.0 to ppc_fp128 + %next = call double @nexttoward(double 1.0, ppc_fp128 %arg) + ret double %next +} + +define float @nexttowardf_down_direction() { +; CHECK-LABEL: define float @nexttowardf_down_direction() { +; CHECK-NEXT: ret float 0x3FEFFFFFE0000000 +; + %arg = fpext float 0.0 to ppc_fp128 + %next = call float @nexttowardf(float 1.0, ppc_fp128 %arg) + ret float %next +} + +define double @nexttoward_pos_overflow() { +; CHECK-LABEL: define double @nexttoward_pos_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0x7FEFFFFFFFFFFFFF, ppc_fp128 0xM7FF00000000000000000000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %pos_max = load double, ptr @dbl_pos_max + %pos_inf = load double, ptr @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 float @nexttowardf_pos_overflow () { +; CHECK-LABEL: define float @nexttowardf_pos_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x47EFFFFFE0000000, ppc_fp128 0xM7FF00000000000000000000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %pos_max = load float, ptr @flt_pos_max + %pos_inf = load float, ptr @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 double @nexttoward_neg_overflow() { +; CHECK-LABEL: define double @nexttoward_neg_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0xFFEFFFFFFFFFFFFF, ppc_fp128 0xMFFF00000000000000000000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %neg_max = load double, ptr @dbl_neg_max + %neg_inf = load double, ptr @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 float @nexttowardf_neg_overflow() { +; CHECK-LABEL: define float @nexttowardf_neg_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0xC7EFFFFFE0000000, ppc_fp128 0xMFFF00000000000000000000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %neg_max = load float, ptr @flt_neg_max + %neg_inf = load float, ptr @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 double @nexttoward_subnormal() { +; CHECK-LABEL: define double @nexttoward_subnormal() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, ppc_fp128 0xM3FF00000000000000000000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %subnormal = load double, ptr @dbl_pos_min_subnormal + %target = fpext double 1.0 to ppc_fp128 + %next = call double @nexttoward(double %subnormal, ppc_fp128 %target) + ret double %next +} + +define float @nexttowardf_subnormal() { +; CHECK-LABEL: define float @nexttowardf_subnormal() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, ppc_fp128 0xM3FF00000000000000000000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %subnormal = load float, ptr @flt_pos_min_subnormal + %target = fpext float 1.0 to ppc_fp128 + %next = call float @nexttowardf(float %subnormal, ppc_fp128 %target) + ret float %next +} + +declare double @nexttoward(double, ppc_fp128) #0 +declare float @nexttowardf(float, ppc_fp128) #0 +attributes #0 = { willreturn memory(errnomem: write) } 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..84a29fe0deb10 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/constant-fold-nexttoward-x86-fp80.ll @@ -0,0 +1,112 @@ +; 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 + +define double @nexttoward_up_direction() { +; CHECK-LABEL: define double @nexttoward_up_direction() { +; CHECK-NEXT: ret double 0x3FF0000000000001 +; + %arg = fpext double 2.0 to x86_fp80 + %next = call double @nexttoward(double 1.0, x86_fp80 %arg) + ret double %next +} + +define float @nexttowardf_up_direction() { +; CHECK-LABEL: define float @nexttowardf_up_direction() { +; CHECK-NEXT: ret float 0x3FF0000020000000 +; + %arg = fpext float 2.0 to x86_fp80 + %next = call float @nexttowardf(float 1.0, x86_fp80 %arg) + ret float %next +} + +define double @nexttoward_down_direction() { +; CHECK-LABEL: define double @nexttoward_down_direction() { +; CHECK-NEXT: ret double 0x3FEFFFFFFFFFFFFF +; + %arg = fpext double 0.0 to x86_fp80 + %next = call double @nexttoward(double 1.0, x86_fp80 %arg) + ret double %next +} + +define float @nexttowardf_down_direction() { +; CHECK-LABEL: define float @nexttowardf_down_direction() { +; CHECK-NEXT: ret float 0x3FEFFFFFE0000000 +; + %arg = fpext float 0.0 to x86_fp80 + %next = call float @nexttowardf(float 1.0, x86_fp80 %arg) + ret float %next +} + +define double @nexttoward_pos_overflow () { +; CHECK-LABEL: define double @nexttoward_pos_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0x7FEFFFFFFFFFFFFF, x86_fp80 0xK7FFF8000000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %pos_max = load double, ptr @dbl_pos_max + %pos_inf = load double, ptr @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 float @nexttowardf_pos_overflow() { +; CHECK-LABEL: define float @nexttowardf_pos_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x47EFFFFFE0000000, x86_fp80 0xK7FFF8000000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %pos_max = load float, ptr @flt_pos_max + %pos_inf = load float, ptr @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 double @nexttoward_neg_overflow() { +; CHECK-LABEL: define double @nexttoward_neg_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 0xFFEFFFFFFFFFFFFF, x86_fp80 0xKFFFF8000000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %neg_max = load double, ptr @dbl_neg_max + %neg_inf = load double, ptr @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 float @nexttowardf_neg_overflow() { +; CHECK-LABEL: define float @nexttowardf_neg_overflow() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0xC7EFFFFFE0000000, x86_fp80 0xKFFFF8000000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %neg_max = load float, ptr @flt_neg_max + %neg_inf = load float, ptr @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 double @nexttoward_subnormal() { +; CHECK-LABEL: define double @nexttoward_subnormal() { +; CHECK-NEXT: [[NEXT:%.*]] = call double @nexttoward(double 4.940660e-324, x86_fp80 0xK3FFF8000000000000000) +; CHECK-NEXT: ret double [[NEXT]] +; + %subnormal = load double, ptr @dbl_pos_min_subnormal + %target = fpext double 1.0 to x86_fp80 + %next = call double @nexttoward(double %subnormal, x86_fp80 %target) + ret double %next +} + +define float @nexttowardf_subnormal() { +; CHECK-LABEL: define float @nexttowardf_subnormal() { +; CHECK-NEXT: [[NEXT:%.*]] = call float @nexttowardf(float 0x36A0000000000000, x86_fp80 0xK3FFF8000000000000000) +; CHECK-NEXT: ret float [[NEXT]] +; + %subnormal = load float, ptr @flt_pos_min_subnormal + %target = fpext float 1.0 to x86_fp80 + %next = call float @nexttowardf(float %subnormal, x86_fp80 %target) + ret float %next +} + +declare double @nexttoward(double, x86_fp80) #0 +declare float @nexttowardf(float, x86_fp80) #0 +attributes #0 = { willreturn memory(errnomem: write) } 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