https://github.com/el-ev updated https://github.com/llvm/llvm-project/pull/153563
>From 23c9920efdb4471fd81e78600c23674c54fa2f1f Mon Sep 17 00:00:00 2001 From: Iris Shi <[email protected]> Date: Thu, 7 May 2026 11:07:50 +0800 Subject: [PATCH] [clang] constexpr `__builtin_elementwise_{max,min}` --- clang/docs/ReleaseNotes.rst | 3 + clang/lib/AST/ByteCode/InterpBuiltin.cpp | 118 ++++++------------- clang/lib/AST/ExprConstant.cpp | 49 +++----- clang/test/Sema/constant-builtins-vector.cpp | 63 +++++++++- 4 files changed, 114 insertions(+), 119 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index cb19b80b7e994..de042ae1cc651 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -230,6 +230,9 @@ Non-comprehensive list of changes in this release - Deprecated float types support from ``__builtin_elementwise_max`` and ``__builtin_elementwise_min``. +- ``__builtin_elementwise_max`` and ``__builtin_elementwise_min`` can now be + used in constant expressions for floating-point arguments. + - Added header ``endian.h`` which contains byte order helpers specified in POSIX - Added #pragma loop licm(disable) for llvm.loop.licm.disable metadata diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 11ca93c251380..e025712662582 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -2621,13 +2621,29 @@ static bool interp__builtin_elementwise_fp_binop( Fn, bool IsScalar = false) { assert((Call->getNumArgs() == 2) || (Call->getNumArgs() == 3)); + + // Single floating-point case. + if (!Call->getArg(0)->getType()->isVectorType()) { + assert(!Call->getArg(1)->getType()->isVectorType()); + assert(Call->getNumArgs() == 2); + const Floating &RHS = S.Stk.pop<Floating>(); + const Floating &LHS = S.Stk.pop<Floating>(); + std::optional<APFloat> Result = + Fn(LHS.getAPFloat(), RHS.getAPFloat(), std::nullopt); + if (!Result) + return false; + Floating R = S.allocFloat(Result->getSemantics()); + R.copy(*Result); + S.Stk.push<Floating>(R); + return true; + } + const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); assert(VT->getElementType()->isFloatingType()); unsigned NumElems = VT->getNumElements(); // Vector case. - assert(Call->getArg(0)->getType()->isVectorType() && - Call->getArg(1)->getType()->isVectorType()); + assert(Call->getArg(1)->getType()->isVectorType()); assert(VT->getElementType() == Call->getArg(1)->getType()->castAs<VectorType>()->getElementType()); assert(VT->getNumElements() == @@ -2819,82 +2835,6 @@ interp__builtin_ia32_pack(InterpState &S, CodePtr, const CallExpr *E, return true; } -static bool interp__builtin_elementwise_maxmin(InterpState &S, CodePtr OpPC, - const CallExpr *Call, - unsigned BuiltinID) { - assert(Call->getNumArgs() == 2); - - QualType Arg0Type = Call->getArg(0)->getType(); - - // TODO: Support floating-point types. - if (!(Arg0Type->isIntegerType() || - (Arg0Type->isVectorType() && - Arg0Type->castAs<VectorType>()->getElementType()->isIntegerType()))) - return false; - - if (!Arg0Type->isVectorType()) { - assert(!Call->getArg(1)->getType()->isVectorType()); - APSInt RHS; - if (!popToAPSInt(S, Call->getArg(1), RHS)) - return false; - APSInt LHS; - if (!popToAPSInt(S, Arg0Type, LHS)) - return false; - APInt Result; - if (BuiltinID == Builtin::BI__builtin_elementwise_max) { - Result = std::max(LHS, RHS); - } else if (BuiltinID == Builtin::BI__builtin_elementwise_min) { - Result = std::min(LHS, RHS); - } else { - llvm_unreachable("Wrong builtin ID"); - } - - pushInteger(S, APSInt(Result, !LHS.isSigned()), Call->getType()); - return true; - } - - // Vector case. - assert(Call->getArg(0)->getType()->isVectorType() && - Call->getArg(1)->getType()->isVectorType()); - const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); - assert(VT->getElementType() == - Call->getArg(1)->getType()->castAs<VectorType>()->getElementType()); - assert(VT->getNumElements() == - Call->getArg(1)->getType()->castAs<VectorType>()->getNumElements()); - assert(VT->getElementType()->isIntegralOrEnumerationType()); - - const Pointer &RHS = S.Stk.pop<Pointer>(); - const Pointer &LHS = S.Stk.pop<Pointer>(); - const Pointer &Dst = S.Stk.peek<Pointer>(); - PrimType ElemT = *S.getContext().classify(VT->getElementType()); - unsigned NumElems = VT->getNumElements(); - for (unsigned I = 0; I != NumElems; ++I) { - APSInt Elem1; - APSInt Elem2; - INT_TYPE_SWITCH_NO_BOOL(ElemT, { - Elem1 = LHS.elem<T>(I).toAPSInt(); - Elem2 = RHS.elem<T>(I).toAPSInt(); - }); - - APSInt Result; - if (BuiltinID == Builtin::BI__builtin_elementwise_max) { - Result = APSInt(std::max(Elem1, Elem2), - Call->getType()->isUnsignedIntegerOrEnumerationType()); - } else if (BuiltinID == Builtin::BI__builtin_elementwise_min) { - Result = APSInt(std::min(Elem1, Elem2), - Call->getType()->isUnsignedIntegerOrEnumerationType()); - } else { - llvm_unreachable("Wrong builtin ID"); - } - - INT_TYPE_SWITCH_NO_BOOL(ElemT, - { Dst.elem<T>(I) = static_cast<T>(Result); }); - } - Dst.initializeAllElements(); - - return true; -} - static bool interp__builtin_ia32_pmul( InterpState &S, CodePtr OpPC, const CallExpr *Call, llvm::function_ref<APInt(const APSInt &, const APSInt &, const APSInt &, @@ -5452,8 +5392,26 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, [](const APSInt &LHS, const APSInt &RHS) { return LHS.rotr(RHS); }); case Builtin::BI__builtin_elementwise_max: - case Builtin::BI__builtin_elementwise_min: - return interp__builtin_elementwise_maxmin(S, OpPC, Call, BuiltinID); + case Builtin::BI__builtin_elementwise_min: { + QualType Arg0Type = Call->getArg(0)->getType(); + QualType ElemType = Arg0Type->isVectorType() + ? Arg0Type->castAs<VectorType>()->getElementType() + : Arg0Type; + bool IsMax = BuiltinID == Builtin::BI__builtin_elementwise_max; + if (ElemType->isIntegerType()) + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [IsMax](const APSInt &LHS, const APSInt &RHS) { + return IsMax ? std::max(LHS, RHS) : std::min(LHS, RHS); + }); + if (ElemType->isRealFloatingType()) + return interp__builtin_elementwise_fp_binop( + S, OpPC, Call, + [IsMax](const APFloat &LHS, const APFloat &RHS, + std::optional<APSInt>) -> std::optional<APFloat> { + return IsMax ? maxnum(LHS, RHS) : minnum(LHS, RHS); + }); + return false; + } case clang::X86::BI__builtin_ia32_phaddw128: case clang::X86::BI__builtin_ia32_phaddw256: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 3f3a80f5b77a3..b800a84a2eaad 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -12867,38 +12867,19 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) { case Builtin::BI__builtin_elementwise_max: case Builtin::BI__builtin_elementwise_min: { - APValue SourceLHS, SourceRHS; - if (!EvaluateAsRValue(Info, E->getArg(0), SourceLHS) || - !EvaluateAsRValue(Info, E->getArg(1), SourceRHS)) - return false; - + bool IsMax = E->getBuiltinCallee() == Builtin::BI__builtin_elementwise_max; QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType(); - - if (!DestEltTy->isIntegerType()) - return false; - - unsigned SourceLen = SourceLHS.getVectorLength(); - SmallVector<APValue, 4> ResultElements; - ResultElements.reserve(SourceLen); - - for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) { - APSInt LHS = SourceLHS.getVectorElt(EltNum).getInt(); - APSInt RHS = SourceRHS.getVectorElt(EltNum).getInt(); - switch (E->getBuiltinCallee()) { - case Builtin::BI__builtin_elementwise_max: - ResultElements.push_back( - APValue(APSInt(std::max(LHS, RHS), - DestEltTy->isUnsignedIntegerOrEnumerationType()))); - break; - case Builtin::BI__builtin_elementwise_min: - ResultElements.push_back( - APValue(APSInt(std::min(LHS, RHS), - DestEltTy->isUnsignedIntegerOrEnumerationType()))); - break; - } - } - - return Success(APValue(ResultElements.data(), ResultElements.size()), E); + if (DestEltTy->isIntegerType()) + return EvaluateBinOpExpr([IsMax](const APSInt &LHS, const APSInt &RHS) { + return IsMax ? std::max(LHS, RHS) : std::min(LHS, RHS); + }); + if (DestEltTy->isRealFloatingType()) + return EvaluateFpBinOpExpr( + [IsMax](const APFloat &LHS, const APFloat &RHS, + std::optional<APSInt>) -> std::optional<APFloat> { + return IsMax ? maxnum(LHS, RHS) : minnum(LHS, RHS); + }); + return false; } case X86::BI__builtin_ia32_vpshldd128: case X86::BI__builtin_ia32_vpshldd256: @@ -19918,7 +19899,8 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { case Builtin::BI__builtin_fmaxf: case Builtin::BI__builtin_fmaxl: case Builtin::BI__builtin_fmaxf16: - case Builtin::BI__builtin_fmaxf128: { + case Builtin::BI__builtin_fmaxf128: + case Builtin::BI__builtin_elementwise_max: { APFloat RHS(0.); if (!EvaluateFloat(E->getArg(0), Result, Info) || !EvaluateFloat(E->getArg(1), RHS, Info)) @@ -19931,7 +19913,8 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { case Builtin::BI__builtin_fminf: case Builtin::BI__builtin_fminl: case Builtin::BI__builtin_fminf16: - case Builtin::BI__builtin_fminf128: { + case Builtin::BI__builtin_fminf128: + case Builtin::BI__builtin_elementwise_min: { APFloat RHS(0.); if (!EvaluateFloat(E->getArg(0), Result, Info) || !EvaluateFloat(E->getArg(1), RHS, Info)) diff --git a/clang/test/Sema/constant-builtins-vector.cpp b/clang/test/Sema/constant-builtins-vector.cpp index 56919ade81e43..58bb7abceca4e 100644 --- a/clang/test/Sema/constant-builtins-vector.cpp +++ b/clang/test/Sema/constant-builtins-vector.cpp @@ -1,10 +1,10 @@ -// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension %s -// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension -triple ppc64-unknown-linux %s -// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension -triple ppc64le-unknown-linux %s +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension -Wno-deprecated-builtins %s +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension -Wno-deprecated-builtins -triple ppc64-unknown-linux %s +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension -Wno-deprecated-builtins -triple ppc64le-unknown-linux %s -// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension %s -fexperimental-new-constant-interpreter -// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension -triple ppc64-unknown-linux %s -fexperimental-new-constant-interpreter -// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension -triple ppc64le-unknown-linux %s -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension -Wno-deprecated-builtins %s -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension -Wno-deprecated-builtins -triple ppc64-unknown-linux %s -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension -Wno-deprecated-builtins -triple ppc64le-unknown-linux %s -fexperimental-new-constant-interpreter #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define LITTLE_END 1 @@ -878,6 +878,16 @@ static_assert(__builtin_elementwise_max(1, 2) == 2); static_assert(__builtin_elementwise_max(-1, 1) == 1); static_assert(__builtin_elementwise_max(1U, 2U) == 2U); static_assert(__builtin_elementwise_max(~0U, 0U) == ~0U); +static_assert(__builtin_elementwise_max(1.0f, 2.0f) == 2.0f); +static_assert(__builtin_elementwise_max(-1.0f, 1.0f) == 1.0f); +static_assert(__builtin_elementwise_max(1.0, 2.0) == 2.0); +static_assert(__builtin_elementwise_max(-1.0, 1.0) == 1.0); +// maxnum returns the non-NaN argument when one operand is NaN. +static_assert(__builtin_elementwise_max(__builtin_nanf(""), 1.0f) == 1.0f); +static_assert(__builtin_elementwise_max(1.0f, __builtin_nanf("")) == 1.0f); +static_assert(__builtin_isnan(__builtin_elementwise_max(__builtin_nanf(""), __builtin_nanf("")))); +static_assert(__builtin_elementwise_max(__builtin_inff(), 1.0f) == __builtin_inff()); +static_assert(__builtin_elementwise_max(-__builtin_inff(), 1.0f) == 1.0f); static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_max((vector4char){1, -2, 3, -4}, (vector4char){4, -3, 2, -1})) == (LITTLE_END ? 0xFF03FE04 : 0x04FE03FF )); static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_max((vector4uchar){1, 2, 3, 4}, (vector4uchar){4, 3, 2, 1})) == 0x04030304U); static_assert(__builtin_bit_cast(unsigned long long, __builtin_elementwise_max((vector4short){1, -2, 3, -4}, (vector4short){4, -3, 2, -1})) == (LITTLE_END ? 0xFFFF0003FFFE0004 : 0x0004FFFE0003FFFF)); @@ -886,10 +896,51 @@ static_assert(__builtin_elementwise_min(1, 2) == 1); static_assert(__builtin_elementwise_min(-1, 1) == -1); static_assert(__builtin_elementwise_min(1U, 2U) == 1U); static_assert(__builtin_elementwise_min(~0U, 0U) == 0U); +static_assert(__builtin_elementwise_min(1.0f, 2.0f) == 1.0f); +static_assert(__builtin_elementwise_min(-1.0f, 1.0f) == -1.0f); +static_assert(__builtin_elementwise_min(1.0, 2.0) == 1.0); +static_assert(__builtin_elementwise_min(-1.0, 1.0) == -1.0); +static_assert(__builtin_elementwise_min(__builtin_nanf(""), 1.0f) == 1.0f); +static_assert(__builtin_elementwise_min(1.0f, __builtin_nanf("")) == 1.0f); +static_assert(__builtin_isnan(__builtin_elementwise_min(__builtin_nanf(""), __builtin_nanf("")))); +static_assert(__builtin_elementwise_min(__builtin_inff(), 1.0f) == 1.0f); +static_assert(__builtin_elementwise_min(-__builtin_inff(), 1.0f) == -__builtin_inff()); static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4char){1, -2, 3, -4}, (vector4char){4, -3, 2, -1})) == (LITTLE_END ? 0xFC02FD01 : 0x01FD02FC)); static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4uchar){1, 2, 3, 4}, (vector4uchar){4, 3, 2, 1})) == 0x01020201U); static_assert(__builtin_bit_cast(unsigned long long, __builtin_elementwise_min((vector4short){1, -2, 3, -4}, (vector4short){4, -3, 2, -1})) == (LITTLE_END ? 0xFFFC0002FFFD0001 : 0x0001FFFD0002FFFC)); +constexpr vector4float maxf_vec = + __builtin_elementwise_max((vector4float){1.0f, -2.0f, 3.0f, -4.0f}, + (vector4float){4.0f, -3.0f, 2.0f, -1.0f}); +static_assert(maxf_vec[0] == 4.0f && maxf_vec[1] == -2.0f && + maxf_vec[2] == 3.0f && maxf_vec[3] == -1.0f); +constexpr vector4float minf_vec = + __builtin_elementwise_min((vector4float){1.0f, -2.0f, 3.0f, -4.0f}, + (vector4float){4.0f, -3.0f, 2.0f, -1.0f}); +static_assert(minf_vec[0] == 1.0f && minf_vec[1] == -3.0f && + minf_vec[2] == 2.0f && minf_vec[3] == -4.0f); +constexpr vector4double maxd_vec = + __builtin_elementwise_max((vector4double){1.0, -2.0, 3.0, -4.0}, + (vector4double){4.0, -3.0, 2.0, -1.0}); +static_assert(maxd_vec[0] == 4.0 && maxd_vec[1] == -2.0 && + maxd_vec[2] == 3.0 && maxd_vec[3] == -1.0); +constexpr vector4double mind_vec = + __builtin_elementwise_min((vector4double){1.0, -2.0, 3.0, -4.0}, + (vector4double){4.0, -3.0, 2.0, -1.0}); +static_assert(mind_vec[0] == 1.0 && mind_vec[1] == -3.0 && + mind_vec[2] == 2.0 && mind_vec[3] == -4.0); + +constexpr vector4float maxf_nan_inf = __builtin_elementwise_max( + (vector4float){__builtin_nanf(""), 1.0f, __builtin_inff(), 1.0f}, + (vector4float){1.0f, __builtin_nanf(""), 1.0f, -__builtin_inff()}); +static_assert(maxf_nan_inf[0] == 1.0f && maxf_nan_inf[1] == 1.0f && + maxf_nan_inf[2] == __builtin_inff() && maxf_nan_inf[3] == 1.0f); +constexpr vector4float minf_nan_inf = __builtin_elementwise_min( + (vector4float){__builtin_nanf(""), 1.0f, __builtin_inff(), 1.0f}, + (vector4float){1.0f, __builtin_nanf(""), 1.0f, -__builtin_inff()}); +static_assert(minf_nan_inf[0] == 1.0f && minf_nan_inf[1] == 1.0f && + minf_nan_inf[2] == 1.0f && minf_nan_inf[3] == -__builtin_inff()); + static_assert(__builtin_elementwise_abs(10) == 10); static_assert(__builtin_elementwise_abs(-10) == 10); static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_abs((vector4char){-1, -2, -3, 4})) == (LITTLE_END ? 0x04030201 : 0x01020304)); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
