Author: Chaitanya Koparkar
Date: 2025-09-04T16:00:06+01:00
New Revision: 79ea31923104e7bbbbee11527b6975f57e9dc937

URL: 
https://github.com/llvm/llvm-project/commit/79ea31923104e7bbbbee11527b6975f57e9dc937
DIFF: 
https://github.com/llvm/llvm-project/commit/79ea31923104e7bbbbee11527b6975f57e9dc937.diff

LOG: [Clang] Enable constexpr handling for builtin elementwise fshl/fshr 
(#153572)

Fixes #153151.

Added: 
    

Modified: 
    clang/docs/LanguageExtensions.rst
    clang/include/clang/Basic/Builtins.td
    clang/lib/AST/ByteCode/InterpBuiltin.cpp
    clang/lib/AST/ExprConstant.cpp
    clang/test/Sema/constant-builtins-vector.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index 9767fde2c65a1..ad190eace5b05 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -875,12 +875,14 @@ of 
diff erent sizes and signs is forbidden in binary and ternary builtins.
                                                 for the comparison.
 T __builtin_elementwise_fshl(T x, T y, T z)     perform a funnel shift left. 
Concatenate x and y (x is the most        integer types
                                                 significant bits of the wide 
value), the combined value is shifted
-                                                left by z, and the most 
significant bits are extracted to produce
+                                                left by z (modulo the bit 
width of the original arguments),
+                                                and the most significant bits 
are extracted to produce
                                                 a result that is the same size 
as the original arguments.
 
 T __builtin_elementwise_fshr(T x, T y, T z)     perform a funnel shift right. 
Concatenate x and y (x is the most       integer types
                                                 significant bits of the wide 
value), the combined value is shifted
-                                                right by z, and the least 
significant bits are extracted to produce
+                                                right by z (modulo the bit 
width of the original arguments),
+                                                and the least significant bits 
are extracted to produce
                                                 a result that is the same size 
as the original arguments.
  T __builtin_elementwise_ctlz(T x[, T y])       return the number of leading 0 
bits in the first argument. If          integer types
                                                 the first argument is 0 and an 
optional second argument is provided,

diff  --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index af0e8242f1e0d..27fc6f008d743 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1540,13 +1540,13 @@ def ElementwiseSubSat : Builtin {
 
 def ElementwiseFshl : Builtin {
   let Spellings = ["__builtin_elementwise_fshl"];
-  let Attributes = [NoThrow, Const, CustomTypeChecking];
+  let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def ElementwiseFshr : Builtin {
   let Spellings = ["__builtin_elementwise_fshr"];
-  let Attributes = [NoThrow, Const, CustomTypeChecking];
+  let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 

diff  --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 8c2b71160f7f3..ff6ef5a1f6864 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2829,6 +2829,65 @@ static bool interp__builtin_select(InterpState &S, 
CodePtr OpPC,
   return true;
 }
 
+static bool interp__builtin_elementwise_fsh(InterpState &S, CodePtr OpPC,
+                                            const CallExpr *Call,
+                                            unsigned BuiltinID) {
+  assert(Call->getNumArgs() == 3);
+
+  QualType Arg0Type = Call->getArg(0)->getType();
+  QualType Arg1Type = Call->getArg(1)->getType();
+  QualType Arg2Type = Call->getArg(2)->getType();
+
+  // Non-vector integer types.
+  if (!Arg0Type->isVectorType()) {
+    const APSInt &Shift =
+        popToAPSInt(S.Stk, *S.getContext().classify(Arg2Type));
+    const APSInt &Lo = popToAPSInt(S.Stk, *S.getContext().classify(Arg1Type));
+    const APSInt &Hi = popToAPSInt(S.Stk, *S.getContext().classify(Arg0Type));
+    APSInt Result;
+    if (BuiltinID == Builtin::BI__builtin_elementwise_fshl)
+      Result = APSInt(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned());
+    else if (BuiltinID == Builtin::BI__builtin_elementwise_fshr)
+      Result = APSInt(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned());
+    else
+      llvm_unreachable("Wrong builtin ID");
+    pushInteger(S, Result, Call->getType());
+    return true;
+  }
+
+  // Vector type.
+  const auto *VecT = Arg0Type->castAs<VectorType>();
+  const PrimType &ElemT = *S.getContext().classify(VecT->getElementType());
+  unsigned NumElems = VecT->getNumElements();
+
+  const Pointer &VecShift = S.Stk.pop<Pointer>();
+  const Pointer &VecLo = S.Stk.pop<Pointer>();
+  const Pointer &VecHi = S.Stk.pop<Pointer>();
+  const Pointer &Dst = S.Stk.peek<Pointer>();
+  for (unsigned I = 0; I != NumElems; ++I) {
+    APSInt Hi;
+    APSInt Lo;
+    APSInt Shift;
+    INT_TYPE_SWITCH_NO_BOOL(ElemT, {
+      Hi = VecHi.elem<T>(I).toAPSInt();
+      Lo = VecLo.elem<T>(I).toAPSInt();
+      Shift = VecShift.elem<T>(I).toAPSInt();
+    });
+    APSInt Result;
+    if (BuiltinID == Builtin::BI__builtin_elementwise_fshl)
+      Result = APSInt(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned());
+    else if (BuiltinID == Builtin::BI__builtin_elementwise_fshr)
+      Result = APSInt(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned());
+    else
+      llvm_unreachable("Wrong builtin ID");
+    INT_TYPE_SWITCH_NO_BOOL(ElemT,
+                            { Dst.elem<T>(I) = static_cast<T>(Result); });
+  }
+  Dst.initializeAllElements();
+
+  return true;
+}
+
 bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
                       uint32_t BuiltinID) {
   if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID))
@@ -3393,6 +3452,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, 
const CallExpr *Call,
   case X86::BI__builtin_ia32_selectpd_512:
     return interp__builtin_select(S, OpPC, Call);
 
+  case Builtin::BI__builtin_elementwise_fshl:
+  case Builtin::BI__builtin_elementwise_fshr:
+    return interp__builtin_elementwise_fsh(S, OpPC, Call, BuiltinID);
+
   default:
     S.FFDiag(S.Current->getLocation(OpPC),
              diag::note_invalid_subexpr_in_const_expr)

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f0592b0cd51c2..6c6909e5b2370 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -12108,6 +12108,40 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr 
*E) {
     }
     return Success(APValue(ResultElements.data(), ResultElements.size()), E);
   }
+
+  case Builtin::BI__builtin_elementwise_fshl:
+  case Builtin::BI__builtin_elementwise_fshr: {
+    APValue SourceHi, SourceLo, SourceShift;
+    if (!EvaluateAsRValue(Info, E->getArg(0), SourceHi) ||
+        !EvaluateAsRValue(Info, E->getArg(1), SourceLo) ||
+        !EvaluateAsRValue(Info, E->getArg(2), SourceShift))
+      return false;
+
+    QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType();
+    if (!DestEltTy->isIntegerType())
+      return false;
+
+    unsigned SourceLen = SourceHi.getVectorLength();
+    SmallVector<APValue> ResultElements;
+    ResultElements.reserve(SourceLen);
+    for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) {
+      const APSInt &Hi = SourceHi.getVectorElt(EltNum).getInt();
+      const APSInt &Lo = SourceLo.getVectorElt(EltNum).getInt();
+      const APSInt &Shift = SourceShift.getVectorElt(EltNum).getInt();
+      switch (E->getBuiltinCallee()) {
+      case Builtin::BI__builtin_elementwise_fshl:
+        ResultElements.push_back(APValue(
+            APSInt(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned())));
+        break;
+      case Builtin::BI__builtin_elementwise_fshr:
+        ResultElements.push_back(APValue(
+            APSInt(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned())));
+        break;
+      }
+    }
+
+    return Success(APValue(ResultElements.data(), ResultElements.size()), E);
+  }
   }
 }
 
@@ -14061,6 +14095,25 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const 
CallExpr *E,
     APInt Result = std::min(LHS, RHS);
     return Success(APSInt(Result, !LHS.isSigned()), E);
   }
+  case Builtin::BI__builtin_elementwise_fshl:
+  case Builtin::BI__builtin_elementwise_fshr: {
+    APSInt Hi, Lo, Shift;
+    if (!EvaluateInteger(E->getArg(0), Hi, Info) ||
+        !EvaluateInteger(E->getArg(1), Lo, Info) ||
+        !EvaluateInteger(E->getArg(2), Shift, Info))
+      return false;
+
+    switch (BuiltinOp) {
+    case Builtin::BI__builtin_elementwise_fshl: {
+      APSInt Result(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned());
+      return Success(Result, E);
+    }
+    case Builtin::BI__builtin_elementwise_fshr: {
+      APSInt Result(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned());
+      return Success(Result, E);
+    }
+    }
+  }
   case Builtin::BIstrlen:
   case Builtin::BIwcslen:
     // A call to strlen is not a constant expression.

diff  --git a/clang/test/Sema/constant-builtins-vector.cpp 
b/clang/test/Sema/constant-builtins-vector.cpp
index 17fa95871740d..714a7fb753214 100644
--- a/clang/test/Sema/constant-builtins-vector.cpp
+++ b/clang/test/Sema/constant-builtins-vector.cpp
@@ -961,3 +961,51 @@ static_assert(fmaDouble1[3] == 26.0);
 constexpr float fmaArray[] = {2.0f, 2.0f, 2.0f, 2.0f};
 constexpr float fmaResult = __builtin_elementwise_fma(fmaArray[1], 
fmaArray[2], fmaArray[3]);
 static_assert(fmaResult == 6.0f, "");
+
+static_assert(__builtin_elementwise_fshl((unsigned char)255, (unsigned char)0, 
(unsigned char)8) == (unsigned char)255);
+static_assert(__builtin_elementwise_fshl((char)127, (char)0, (char)8) == 
(char)127);
+static_assert(__builtin_elementwise_fshl((unsigned char)0, (unsigned char)255, 
(unsigned char)8) == (unsigned char)0);
+static_assert(__builtin_elementwise_fshl((char)0, (char)127, (char)8) == 
(char)0);
+static_assert(__builtin_elementwise_fshr((unsigned char)255, (unsigned char)0, 
(unsigned char)8) == (unsigned char)0);
+static_assert(__builtin_elementwise_fshr((char)127, (char)0, (char)8) == 
(char)0);
+static_assert(__builtin_elementwise_fshr((unsigned char)0, (unsigned char)255, 
(unsigned char)8) == (unsigned char)255);
+static_assert(__builtin_elementwise_fshr((char)0, (char)127, (char)8) == 
(char)127);
+static_assert(__builtin_elementwise_fshl((unsigned int)4294967295, (unsigned 
int)0, (unsigned int)32) == (unsigned int)4294967295);
+static_assert(__builtin_elementwise_fshl((int)2147483647, (int)0, (int)32) == 
(int)2147483647);
+static_assert(__builtin_elementwise_fshl((unsigned int)0, (unsigned 
int)4294967295, (unsigned int)32) == (unsigned int)0);
+static_assert(__builtin_elementwise_fshl((int)0, (int)2147483647, (int)32) == 
(int)0);
+static_assert(__builtin_elementwise_fshr((unsigned int)4294967295, (unsigned 
int)0, (unsigned int)32) == (unsigned int)0);
+static_assert(__builtin_elementwise_fshr((int)2147483647, (int)0, (int)32) == 
(int)0);
+static_assert(__builtin_elementwise_fshr((unsigned int)0, (unsigned 
int)4294967295, (unsigned int)32) == (unsigned int)4294967295);
+static_assert(__builtin_elementwise_fshr((int)0, (int)2147483647, (int)32) == 
(int)2147483647);
+static_assert(__builtin_elementwise_fshl((unsigned long 
long)18446744073709551615ULL, (unsigned long long)0, (unsigned long long)64) == 
(unsigned long long)18446744073709551615ULL);
+static_assert(__builtin_elementwise_fshl((long long)9223372036854775807, (long 
long)0, (long long)64) == (long long)9223372036854775807);
+static_assert(__builtin_elementwise_fshl((unsigned long long)0, (unsigned long 
long)18446744073709551615ULL, (unsigned long long)64) == (unsigned long long)0);
+static_assert(__builtin_elementwise_fshl((long long)0, (long 
long)9223372036854775807, (long long)64) == (long long)0);
+static_assert(__builtin_elementwise_fshr((unsigned long 
long)18446744073709551615ULL, (unsigned long long)0, (unsigned long long)64) == 
(unsigned long long)0);
+static_assert(__builtin_elementwise_fshr((long long)9223372036854775807, (long 
long)0, (long long)64) == (long long)0);
+static_assert(__builtin_elementwise_fshr((unsigned long long)0, (unsigned long 
long)18446744073709551615ULL, (unsigned long long)64) == (unsigned long 
long)18446744073709551615ULL);
+static_assert(__builtin_elementwise_fshr((long long)0, (long 
long)9223372036854775807, (long long)64) == (long long)9223372036854775807);
+static_assert(__builtin_elementwise_fshl((short) 1, (short) 2, (short) 3) == 
(short)8);
+static_assert(__builtin_elementwise_fshl((short) 2, (short) 1, (short) 3) == 
(short)16);
+static_assert(__builtin_elementwise_fshl(1, 2 , 2) == 4);
+static_assert(__builtin_elementwise_fshl(2L, 1L , 2L) == 8L);
+static_assert(__builtin_elementwise_fshr((unsigned char)1, (unsigned char)2, 
(unsigned char)3) == (unsigned char)32);
+constexpr vector4uchar v4s_fshl_var =
+  __builtin_elementwise_fshl((vector4uchar){255, 15, 0, 2},
+                             (vector4uchar){0, 15, 255, 1},
+                             (vector4uchar){15, 11, 8, 3});
+static_assert(v4s_fshl_var[0] == 128);
+static_assert(v4s_fshl_var[1] == 120);
+static_assert(v4s_fshl_var[2] == 0);
+static_assert(v4s_fshl_var[3] == 16);
+constexpr vector4uchar v4s_fshr_var =
+  __builtin_elementwise_fshr((vector4uchar){255, 15, 0, 1},
+                             (vector4uchar){0, 15, 255, 2},
+                             (vector4uchar){15, 11, 8, 3});
+static_assert(v4s_fshr_var[0] == 254);
+static_assert(v4s_fshr_var[1] == 225);
+static_assert(v4s_fshr_var[2] == 255);
+static_assert(v4s_fshr_var[3] == 32);
+static_assert(__builtin_elementwise_fshl(v4s_fshl_var[0], v4s_fshl_var[1], 
v4s_fshl_var[2]) == 128);
+static_assert(__builtin_elementwise_fshr(v4s_fshr_var[0], v4s_fshr_var[1], 
v4s_fshr_var[2]) == 253);


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to