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

Reply via email to