diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -273,6 +273,18 @@ BUILTIN(__builtin_fminf16, "hhh", "FncE") BUILTIN(__builtin_fminl, "LdLdLd", "FncE") BUILTIN(__builtin_fminf128, "LLdLLdLLd", "FncE") +BUILTIN(__builtin_fmaximum, "ddd", "FncE") +BUILTIN(__builtin_fmaximumf, "fff", "FncE") +BUILTIN(__builtin_fmaximumf16, "hhh", "FncE") +BUILTIN(__builtin_fmaximumbf16, "yyy", "FncE") +BUILTIN(__builtin_fmaximuml, "LdLdLd", "FncE") +BUILTIN(__builtin_fmaximumf128, "LLdLLdLLd", "FncE") +BUILTIN(__builtin_fminimum, "ddd", "FncE") +BUILTIN(__builtin_fminimumf, "fff", "FncE") +BUILTIN(__builtin_fminimumf16, "hhh", "FncE") +BUILTIN(__builtin_fminimumbf16, "yyy", "FncE") +BUILTIN(__builtin_fminimuml, "LdLdLd", "FncE") +BUILTIN(__builtin_fminimumf128, "LLdLLdLLd", "FncE") BUILTIN(__builtin_hypot , "ddd" , "Fne") BUILTIN(__builtin_hypotf, "fff" , "Fne") BUILTIN(__builtin_hypotl, "LdLdLd", "Fne") diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14310,6 +14310,34 @@ Result = RHS; return true; } + + case Builtin::BI__builtin_fmaximum: + case Builtin::BI__builtin_fmaximumf: + case Builtin::BI__builtin_fmaximuml: + case Builtin::BI__builtin_fmaximumf16: + case Builtin::BI__builtin_fmaximumbf16: + case Builtin::BI__builtin_fmaximumf128: { + APFloat RHS(0.); + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateFloat(E->getArg(1), RHS, Info)) + return false; + Result = maximum(Result, RHS); + return true; + } + + case Builtin::BI__builtin_fminimum: + case Builtin::BI__builtin_fminimumf: + case Builtin::BI__builtin_fminimuml: + case Builtin::BI__builtin_fminimumf16: + case Builtin::BI__builtin_fminimumbf16: + case Builtin::BI__builtin_fminimumf128: { + APFloat RHS(0.); + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateFloat(E->getArg(1), RHS, Info)) + return false; + Result = minimum(Result, RHS); + return true; + } } } diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -290,6 +290,54 @@ return true; } +/// Implements IEEE 754-2018 minimum semantics. Returns the smaller of 2 +/// arguments, propagating NaNs and treating -0 as less than +0. +static bool interp__builtin_fminimum(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const Function *F) { + const Floating &LHS = getParam(Frame, 0); + const Floating &RHS = getParam(Frame, 1); + + Floating Result; + + if (LHS.isNan()) + Result = LHS; + else if (RHS.isNan()) + Result = RHS; + else if (LHS.isZero() && RHS.isZero() && + (LHS.isNegative() != RHS.isNegative())) + Result = LHS.isNegative() ? LHS : RHS; + else + Result = RHS < LHS ? RHS : LHS; + + S.Stk.push(Result); + return true; +} + +/// Implements IEEE 754-2018 maximum semantics. Returns the smaller of 2 +/// arguments, propagating NaNs and treating -0 as less than +0. +static bool interp__builtin_fmaximum(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const Function *Func) { + const Floating &LHS = getParam(Frame, 0); + const Floating &RHS = getParam(Frame, 1); + + Floating Result; + + if (LHS.isNan()) + Result = LHS; + else if (RHS.isNan()) + Result = RHS; + else if (LHS.isZero() && RHS.isZero() && + (LHS.isNegative() != RHS.isNegative())) + Result = LHS.isNegative() ? RHS : LHS; + else + Result = LHS < RHS ? RHS : LHS; + + S.Stk.push(Result); + return true; +} + /// Defined as __builtin_isnan(...), to accommodate the fact that it can /// take a float, double, long double, etc. /// But for us, that's all a Floating anyway. @@ -472,6 +520,26 @@ return Ret(S, OpPC, Dummy); break; + case Builtin::BI__builtin_fminimum: + case Builtin::BI__builtin_fminimumf: + case Builtin::BI__builtin_fminimuml: + case Builtin::BI__builtin_fminimumf16: + case Builtin::BI__builtin_fminimumbf16: + case Builtin::BI__builtin_fminimumf128: + if (interp__builtin_fminimum(S, OpPC, Frame, F)) + return Ret(S, OpPC, Dummy); + break; + + case Builtin::BI__builtin_fmaximum: + case Builtin::BI__builtin_fmaximumf: + case Builtin::BI__builtin_fmaximuml: + case Builtin::BI__builtin_fmaximumf16: + case Builtin::BI__builtin_fmaximumbf16: + case Builtin::BI__builtin_fmaximumf128: + if (interp__builtin_fmaximum(S, OpPC, Frame, F)) + return Ret(S, OpPC, Dummy); + break; + case Builtin::BI__builtin_isnan: if (interp__builtin_isnan(S, OpPC, Frame, F)) return retInt(S, OpPC, Dummy); diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2443,6 +2443,26 @@ Intrinsic::minnum, Intrinsic::experimental_constrained_minnum)); + case Builtin::BI__builtin_fmaximum: + case Builtin::BI__builtin_fmaximumf: + case Builtin::BI__builtin_fmaximumf16: + case Builtin::BI__builtin_fmaximumbf16: + case Builtin::BI__builtin_fmaximuml: + case Builtin::BI__builtin_fmaximumf128: + return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(*this, E, + Intrinsic::maximum, + Intrinsic::experimental_constrained_maximum)); + + case Builtin::BI__builtin_fminimum: + case Builtin::BI__builtin_fminimumf: + case Builtin::BI__builtin_fminimumf16: + case Builtin::BI__builtin_fminimumbf16: + case Builtin::BI__builtin_fminimuml: + case Builtin::BI__builtin_fminimumf128: + return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(*this, E, + Intrinsic::minimum, + Intrinsic::experimental_constrained_minimum)); + // fmod() is a special-case. It maps to the frem instruction rather than an // LLVM intrinsic. case Builtin::BIfmod: diff --git a/clang/test/CodeGen/builtins.c b/clang/test/CodeGen/builtins.c --- a/clang/test/CodeGen/builtins.c +++ b/clang/test/CodeGen/builtins.c @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -emit-llvm -o %t %s +// RUN: %clang_cc1 -emit-llvm -o %t %s -Wno-format // RUN: not grep __builtin %t -// RUN: %clang_cc1 -emit-llvm -triple x86_64-darwin-apple -o - %s | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple x86_64-darwin-apple -o - %s -Wno-format | FileCheck %s int printf(const char *, ...); @@ -276,6 +276,8 @@ // CHECK-LABEL: define{{.*}} void @test_float_builtin_ops void test_float_builtin_ops(float F, double D, long double LD) { + volatile __fp16 resf16 = F; + volatile __bf16 resbf16 = F; volatile float resf; volatile double resd; volatile long double resld; @@ -323,6 +325,36 @@ resld = __builtin_fmaxl(LD, LD); // CHECK: call x86_fp80 @llvm.maxnum.f80 + resf16 = __builtin_fminimumf16(F, F); + // CHECK: call half @llvm.minimum.f16 + + resbf16 = __builtin_fminimumbf16(F, F); + // CHECK: call bfloat @llvm.minimum.bf16 + + resf = __builtin_fminimumf(F, F); + // CHECK: call float @llvm.minimum.f32 + + resd = __builtin_fminimum(D, D); + // CHECK: call double @llvm.minimum.f64 + + resld = __builtin_fminimuml(LD, LD); + // CHECK: call x86_fp80 @llvm.minimum.f80 + + resf16 = __builtin_fmaximumf16(F, F); + // CHECK: call half @llvm.maximum.f16 + + resbf16 = __builtin_fmaximumbf16(F, F); + // CHECK: call bfloat @llvm.maximum.bf16 + + resf = __builtin_fmaximumf(F, F); + // CHECK: call float @llvm.maximum.f32 + + resd = __builtin_fmaximum(D, D); + // CHECK: call double @llvm.maximum.f64 + + resld = __builtin_fmaximuml(LD, LD); + // CHECK: call x86_fp80 @llvm.maximum.f80 + resf = __builtin_fabsf(F); // CHECK: call float @llvm.fabs.f32 @@ -413,7 +445,7 @@ resld = __builtin_roundevenl(LD); // CHECK: call x86_fp80 @llvm.roundeven.f80 - + resli = __builtin_lroundf (F); // CHECK: call i64 @llvm.lround.i64.f32 diff --git a/clang/test/CodeGen/math-builtins.c b/clang/test/CodeGen/math-builtins.c --- a/clang/test/CodeGen/math-builtins.c +++ b/clang/test/CodeGen/math-builtins.c @@ -395,6 +395,37 @@ // HAS_ERRNO: declare x86_fp80 @llvm.minnum.f80(x86_fp80, x86_fp80) [[READNONE_INTRINSIC]] // HAS_ERRNO: declare fp128 @llvm.minnum.f128(fp128, fp128) [[READNONE_INTRINSIC]] +__builtin_fmaximum(f,f); __builtin_fmaximumf(f,f); __builtin_fmaximuml(f,f); +__builtin_fmaximumf128(f,f); __builtin_fmaximumf16(f,f); __builtin_fmaximumbf16(f,f); +// NO__ERRNO: declare double @llvm.maximum.f64(double, double) [[READNONE_INTRINSIC]] +// NO__ERRNO: declare float @llvm.maximum.f32(float, float) [[READNONE_INTRINSIC]] +// NO__ERRNO: declare x86_fp80 @llvm.maximum.f80(x86_fp80, x86_fp80) [[READNONE_INTRINSIC]] +// NO__ERRNO: declare fp128 @llvm.maximum.f128(fp128, fp128) [[READNONE_INTRINSIC]] +// NO__ERRNO: declare half @llvm.maximum.f16(half, half) [[READNONE_INTRINSIC]] +// NO__ERRNO: declare bfloat @llvm.maximum.bf16(bfloat, bfloat) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare double @llvm.maximum.f64(double, double) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare float @llvm.maximum.f32(float, float) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare x86_fp80 @llvm.maximum.f80(x86_fp80, x86_fp80) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare fp128 @llvm.maximum.f128(fp128, fp128) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare half @llvm.maximum.f16(half, half) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare bfloat @llvm.maximum.bf16(bfloat, bfloat) [[READNONE_INTRINSIC]] + +__builtin_fminimum(f,f); __builtin_fminimumf(f,f); __builtin_fminimuml(f,f); +__builtin_fminimumf128(f,f); __builtin_fminimumf16(f,f); __builtin_fminimumbf16(f,f); + +// NO__ERRNO: declare double @llvm.minimum.f64(double, double) [[READNONE_INTRINSIC]] +// NO__ERRNO: declare float @llvm.minimum.f32(float, float) [[READNONE_INTRINSIC]] +// NO__ERRNO: declare x86_fp80 @llvm.minimum.f80(x86_fp80, x86_fp80) [[READNONE_INTRINSIC]] +// NO__ERRNO: declare fp128 @llvm.minimum.f128(fp128, fp128) [[READNONE_INTRINSIC]] +// NO__ERRNO: declare half @llvm.minimum.f16(half, half) [[READNONE_INTRINSIC]] +// NO__ERRNO: declare bfloat @llvm.minimum.bf16(bfloat, bfloat) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare double @llvm.minimum.f64(double, double) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare float @llvm.minimum.f32(float, float) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare x86_fp80 @llvm.minimum.f80(x86_fp80, x86_fp80) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare fp128 @llvm.minimum.f128(fp128, fp128) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare half @llvm.minimum.f16(half, half) [[READNONE_INTRINSIC]] +// HAS_ERRNO: declare bfloat @llvm.minimum.bf16(bfloat, bfloat) [[READNONE_INTRINSIC]] + __builtin_hypot(f,f); __builtin_hypotf(f,f); __builtin_hypotl(f,f); __builtin_hypotf128(f,f); // NO__ERRNO: declare double @hypot(double noundef, double noundef) [[READNONE]] diff --git a/clang/test/Sema/constant-builtins-fmaximum.cpp b/clang/test/Sema/constant-builtins-fmaximum.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/constant-builtins-fmaximum.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s +// expected-no-diagnostics + +constexpr double NaN = __builtin_nan(""); +constexpr double Inf = __builtin_inf(); +constexpr double NegInf = -__builtin_inf(); + +#define FMAX_TEST_SIMPLE(T, FUNC) \ + static_assert(T(6.7890) == FUNC(T(1.2345), T(6.7890))); \ + static_assert(T(6.7890) == FUNC(T(6.7890), T(1.2345))); + +#define FMAX_TEST_NAN(T, FUNC) \ + static_assert(__builtin_isnan(FUNC(NaN, Inf))); \ + static_assert(__builtin_isnan(FUNC(NegInf, NaN))); \ + static_assert(__builtin_isnan(FUNC(NaN, 0.0))); \ + static_assert(__builtin_isnan(FUNC(-0.0, NaN))); \ + static_assert(__builtin_isnan(FUNC(NaN, T(-1.2345)))); \ + static_assert(__builtin_isnan(FUNC(T(1.2345), NaN))); \ + static_assert(__builtin_isnan(FUNC(NaN, NaN))); + +#define FMAX_TEST_INF(T, FUNC) \ + static_assert(Inf == FUNC(NegInf, Inf)); \ + static_assert(Inf == FUNC(Inf, 0.0)); \ + static_assert(Inf == FUNC(-0.0, Inf)); \ + static_assert(Inf == FUNC(Inf, T(1.2345))); \ + static_assert(Inf == FUNC(T(-1.2345), Inf)); + +#define FMAX_TEST_NEG_INF(T, FUNC) \ + static_assert(Inf == FUNC(Inf, NegInf)); \ + static_assert(0.0 == FUNC(NegInf, 0.0)); \ + static_assert(-0.0 == FUNC(-0.0, NegInf)); \ + static_assert(T(-1.2345) == FUNC(NegInf, T(-1.2345))); \ + static_assert(T(1.2345) == FUNC(T(1.2345), NegInf)); + +#define FMAX_TEST_BOTH_ZERO(T, FUNC) \ + static_assert(__builtin_copysign(1.0, FUNC(0.0, 0.0)) == 1.0); \ + static_assert(__builtin_copysign(1.0, FUNC(-0.0, 0.0)) == 1.0); \ + static_assert(__builtin_copysign(1.0, FUNC(0.0, -0.0)) == 1.0); \ + static_assert(__builtin_copysign(1.0, FUNC(-0.0, -0.0)) == -1.0); + +#define LIST_FMAX_TESTS(T, FUNC) \ + FMAX_TEST_SIMPLE(T, FUNC) \ + FMAX_TEST_NAN(T, FUNC) \ + FMAX_TEST_INF(T, FUNC) \ + FMAX_TEST_NEG_INF(T, FUNC) \ + FMAX_TEST_BOTH_ZERO(T, FUNC) + +LIST_FMAX_TESTS(double, __builtin_fmaximum) +LIST_FMAX_TESTS(float, __builtin_fmaximumf) +LIST_FMAX_TESTS((long double), __builtin_fmaximuml) +LIST_FMAX_TESTS(__fp16, __builtin_fmaximumf16) +LIST_FMAX_TESTS(__bf16, __builtin_fmaximumbf16) +#ifdef __FLOAT128__ +LIST_FMAX_TESTS(__float128, __builtin_fmaximumf128) +#endif diff --git a/clang/test/Sema/constant-builtins-fminimum.cpp b/clang/test/Sema/constant-builtins-fminimum.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/constant-builtins-fminimum.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s +// expected-no-diagnostics + +constexpr double NaN = __builtin_nan(""); +constexpr double Inf = __builtin_inf(); +constexpr double NegInf = -__builtin_inf(); + +#define FMIN_TEST_SIMPLE(T, FUNC) \ + static_assert(T(1.2345) == FUNC(T(1.2345), T(6.7890))); \ + static_assert(T(1.2345) == FUNC(T(6.7890), T(1.2345))); + +#define FMIN_TEST_NAN(T, FUNC) \ + static_assert(__builtin_isnan(FUNC(NaN, Inf))); \ + static_assert(__builtin_isnan(FUNC(NegInf, NaN))); \ + static_assert(__builtin_isnan(FUNC(NaN, 0.0))); \ + static_assert(__builtin_isnan(FUNC(-0.0, NaN))); \ + static_assert(__builtin_isnan(FUNC(NaN, T(-1.2345)))); \ + static_assert(__builtin_isnan(FUNC(T(1.2345), NaN))); \ + static_assert(__builtin_isnan(FUNC(NaN, NaN))); + +#define FMIN_TEST_INF(T, FUNC) \ + static_assert(NegInf == FUNC(NegInf, Inf)); \ + static_assert(0.0 == FUNC(Inf, 0.0)); \ + static_assert(-0.0 == FUNC(-0.0, Inf)); \ + static_assert(T(1.2345) == FUNC(Inf, T(1.2345))); \ + static_assert(T(-1.2345) == FUNC(T(-1.2345), Inf)); + +#define FMIN_TEST_NEG_INF(T, FUNC) \ + static_assert(NegInf == FUNC(Inf, NegInf)); \ + static_assert(NegInf == FUNC(NegInf, 0.0)); \ + static_assert(NegInf == FUNC(-0.0, NegInf)); \ + static_assert(NegInf == FUNC(NegInf, T(-1.2345))); \ + static_assert(NegInf == FUNC(T(1.2345), NegInf)); + +#define FMIN_TEST_BOTH_ZERO(T, FUNC) \ + static_assert(__builtin_copysign(1.0, FUNC(0.0, 0.0)) == 1.0); \ + static_assert(__builtin_copysign(1.0, FUNC(-0.0, 0.0)) == -1.0); \ + static_assert(__builtin_copysign(1.0, FUNC(0.0, -0.0)) == -1.0); \ + static_assert(__builtin_copysign(1.0, FUNC(-0.0, -0.0)) == -1.0); + +#define LIST_FMIN_TESTS(T, FUNC) \ + FMIN_TEST_SIMPLE(T, FUNC) \ + FMIN_TEST_NAN(T, FUNC) \ + FMIN_TEST_INF(T, FUNC) \ + FMIN_TEST_NEG_INF(T, FUNC) \ + FMIN_TEST_BOTH_ZERO(T, FUNC) + +LIST_FMIN_TESTS(double, __builtin_fminimum) +LIST_FMIN_TESTS(float, __builtin_fminimumf) +LIST_FMIN_TESTS((long double), __builtin_fminimuml) +LIST_FMIN_TESTS(__fp16, __builtin_fminimumf16) +LIST_FMIN_TESTS(__bf16, __builtin_fminimumbf16) +#ifdef __FLOAT128__ +LIST_FMIN_TESTS(__float128, __builtin_fminimumf128) +#endif diff --git a/clang/test/Sema/overloaded-math-builtins.c b/clang/test/Sema/overloaded-math-builtins.c --- a/clang/test/Sema/overloaded-math-builtins.c +++ b/clang/test/Sema/overloaded-math-builtins.c @@ -19,3 +19,21 @@ int *r6 = __builtin_fminf(f, v); // expected-error@-1 {{passing 'float4' (vector of 4 'float' values) to parameter of incompatible type 'float'}} } + +float test_fminimumf(float f, int i, int *ptr, float4 v) { + float r1 = __builtin_fminimumf(f, ptr); + // expected-error@-1 {{passing 'int *' to parameter of incompatible type 'float'}} + float r2 = __builtin_fminimumf(ptr, f); + // expected-error@-1 {{passing 'int *' to parameter of incompatible type 'float'}} + float r3 = __builtin_fminimumf(v, f); + // expected-error@-1 {{passing 'float4' (vector of 4 'float' values) to parameter of incompatible type 'float'}} + float r4 = __builtin_fminimumf(f, v); + // expected-error@-1 {{passing 'float4' (vector of 4 'float' values) to parameter of incompatible type 'float'}} + + + int *r5 = __builtin_fminimumf(f, f); + // expected-error@-1 {{initializing 'int *' with an expression of incompatible type 'float'}} + + int *r6 = __builtin_fminimumf(f, v); + // expected-error@-1 {{passing 'float4' (vector of 4 'float' values) to parameter of incompatible type 'float'}} +}