[clang] [llvm] [Clang] Enable constexpr handling for builtin elementwise fshl/fshr (PR #153572)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 14 05:44:12 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Chaitanya Koparkar (ckoparkar)
<details>
<summary>Changes</summary>
Fixes #<!-- -->153151.
---
Full diff: https://github.com/llvm/llvm-project/pull/153572.diff
6 Files Affected:
- (modified) clang/include/clang/Basic/Builtins.td (+2-2)
- (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+81)
- (modified) clang/lib/AST/ExprConstShared.h (+4)
- (modified) clang/lib/AST/ExprConstant.cpp (+77)
- (modified) clang/test/Sema/constant-builtins-vector.cpp (+48)
- (modified) llvm/include/llvm/ADT/APSInt.h (+6)
``````````diff
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 84206cf8b368b..c5ad9a5d59dea 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1516,13 +1516,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 307b77846969f..015f0443ede54 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2399,6 +2399,83 @@ static bool interp__builtin_elementwise_maxmin(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);
+
+ const QualType Arg1Type = Call->getArg(0)->getType();
+ const QualType Arg2Type = Call->getArg(1)->getType();
+ const QualType Arg3Type = Call->getArg(2)->getType();
+
+ // Non-vector integer types.
+ if (!Arg1Type->isVectorType()) {
+ assert(!Arg2Type->isVectorType());
+ assert(!Arg3Type->isVectorType());
+
+ APSInt Shift = popToAPSInt(
+ S.Stk, *S.getContext().classify(Call->getArg(2)->getType()));
+ APSInt Lo = popToAPSInt(
+ S.Stk, *S.getContext().classify(Call->getArg(1)->getType()));
+ APSInt Hi = popToAPSInt(
+ S.Stk, *S.getContext().classify(Call->getArg(0)->getType()));
+ APSInt Result;
+ if (BuiltinID == Builtin::BI__builtin_elementwise_fshl) {
+ Result = HandleFshl(Hi, Lo, Shift);
+ } else if (BuiltinID == Builtin::BI__builtin_elementwise_fshr) {
+ Result = HandleFshr(Hi, Lo, Shift);
+ } else {
+ llvm_unreachable("Wrong builtin ID");
+ }
+ pushInteger(S, Result, Call->getType());
+ return true;
+ }
+
+ // Vector type.
+ assert(Arg1Type->isVectorType() &&
+ Arg2Type->isVectorType() &&
+ Arg3Type->isVectorType());
+
+ const VectorType *VecT = Arg1Type->castAs<VectorType>();
+ PrimType ElemT = *S.getContext().classify(VecT->getElementType());
+ unsigned NumElems = VecT->getNumElements();
+
+ assert(VecT->getElementType() ==
+ Arg2Type->castAs<VectorType>()->getElementType() &&
+ VecT->getElementType() ==
+ Arg3Type->castAs<VectorType>()->getElementType());
+ assert(NumElems == Arg2Type->castAs<VectorType>()->getNumElements() &&
+ NumElems == Arg3Type->castAs<VectorType>()->getNumElements());
+ assert(VecT->getElementType()->isIntegralOrEnumerationType());
+
+ 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 = HandleFshl(Hi, Lo, Shift);
+ } else if (BuiltinID == Builtin::BI__builtin_elementwise_fshr) {
+ Result = HandleFshr(Hi, Lo, Shift);
+ } 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))
@@ -2810,6 +2887,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
case Builtin::BI__builtin_elementwise_min:
return interp__builtin_elementwise_maxmin(S, OpPC, Call, BuiltinID);
+ 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/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index 401ae629c86bf..a1c5607ced700 100644
--- a/clang/lib/AST/ExprConstShared.h
+++ b/clang/lib/AST/ExprConstShared.h
@@ -18,6 +18,7 @@
namespace llvm {
class APFloat;
+class APSInt;
}
namespace clang {
class QualType;
@@ -71,6 +72,9 @@ void HandleComplexComplexDiv(llvm::APFloat A, llvm::APFloat B, llvm::APFloat C,
llvm::APFloat D, llvm::APFloat &ResR,
llvm::APFloat &ResI);
+llvm::APSInt HandleFshl(llvm::APSInt Hi, llvm::APSInt Lo, llvm::APSInt Shift);
+llvm::APSInt HandleFshr(llvm::APSInt Hi, llvm::APSInt Lo, llvm::APSInt Shift);
+
CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E,
UnaryExprOrTypeTrait ExprKind);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 36dd0f5d7a065..083d84f7375fe 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11723,6 +11723,39 @@ 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) {
+ APSInt Hi = SourceHi.getVectorElt(EltNum).getInt();
+ APSInt Lo = SourceLo.getVectorElt(EltNum).getInt();
+ APSInt Shift = SourceShift.getVectorElt(EltNum).getInt();
+ switch (E->getBuiltinCallee()) {
+ case Builtin::BI__builtin_elementwise_fshl:
+ ResultElements.push_back(APValue(HandleFshl(Hi, Lo, Shift)));
+ break;
+ case Builtin::BI__builtin_elementwise_fshr:
+ ResultElements.push_back(APValue(HandleFshr(Hi, Lo, Shift)));
+ break;
+ }
+ }
+
+ return Success(APValue(ResultElements.data(), ResultElements.size()), E);
+ }
}
}
@@ -13638,6 +13671,28 @@ 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: {
+ if (!E->getArg(0)->isPRValue() ||
+ !E->getArg(1)->isPRValue() ||
+ !E->getArg(2)->isPRValue())
+ return false;
+ 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 = HandleFshl(Hi, Lo, Shift);
+ return Success(Result, E);
+ }
+ case Builtin::BI__builtin_elementwise_fshr: {
+ APSInt Result = HandleFshr(Hi, Lo, Shift);
+ return Success(Result, E);
+ }
+ }
+ }
case Builtin::BIstrlen:
case Builtin::BIwcslen:
// A call to strlen is not a constant expression.
@@ -16405,6 +16460,28 @@ void HandleComplexComplexDiv(APFloat A, APFloat B, APFloat C, APFloat D,
}
}
+// fshl(X,Y,Z): (X << (Z % BW)) | (Y >> (BW - (Z % BW)))
+APSInt HandleFshl(APSInt Hi, APSInt Lo, APSInt Shift) {
+ bool IsUnsigned = Hi.isUnsigned();
+ APSInt BitWidth(llvm::APInt(Hi.getBitWidth(),
+ static_cast<uint64_t>(Hi.getBitWidth())),
+ IsUnsigned);
+ return APSInt((Hi << (Shift % BitWidth)) |
+ (Lo >> (BitWidth - (Shift % BitWidth))),
+ IsUnsigned);
+}
+
+// fshr(X,Y,Z): (X << (BW - (Z % BW))) | (Y >> (Z % BW))
+APSInt HandleFshr(APSInt Hi, APSInt Lo, APSInt Shift) {
+ bool IsUnsigned = Hi.isUnsigned();
+ APSInt BitWidth(llvm::APInt(Hi.getBitWidth(),
+ static_cast<uint64_t>(Hi.getBitWidth())),
+ IsUnsigned);
+ return APSInt((Hi << (BitWidth - (Shift % BitWidth))) |
+ (Lo >> (Shift % BitWidth)),
+ IsUnsigned);
+}
+
bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma)
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
diff --git a/clang/test/Sema/constant-builtins-vector.cpp b/clang/test/Sema/constant-builtins-vector.cpp
index bc575dca98d77..c5f9c3956780e 100644
--- a/clang/test/Sema/constant-builtins-vector.cpp
+++ b/clang/test/Sema/constant-builtins-vector.cpp
@@ -876,3 +876,51 @@ static_assert(__builtin_elementwise_min(~0U, 0U) == 0U);
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));
+
+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);
diff --git a/llvm/include/llvm/ADT/APSInt.h b/llvm/include/llvm/ADT/APSInt.h
index 88a7a6e71c817..6112abed92100 100644
--- a/llvm/include/llvm/ADT/APSInt.h
+++ b/llvm/include/llvm/ADT/APSInt.h
@@ -152,6 +152,9 @@ class [[nodiscard]] APSInt : public APInt {
APSInt operator>>(unsigned Amt) const {
return IsUnsigned ? APSInt(lshr(Amt), true) : APSInt(ashr(Amt), false);
}
+ APSInt operator>>(const APSInt &Amt) const {
+ return IsUnsigned ? APSInt(lshr(Amt), true) : APSInt(ashr(Amt), false);
+ }
APSInt &operator>>=(unsigned Amt) {
if (IsUnsigned)
lshrInPlace(Amt);
@@ -211,6 +214,9 @@ class [[nodiscard]] APSInt : public APInt {
APSInt operator<<(unsigned Bits) const {
return APSInt(static_cast<const APInt &>(*this) << Bits, IsUnsigned);
}
+ APSInt operator<<(const APSInt &Amt) const {
+ return APSInt(static_cast<const APInt &>(*this) << Amt, IsUnsigned);
+ }
APSInt &operator<<=(unsigned Amt) {
static_cast<APInt &>(*this) <<= Amt;
return *this;
``````````
</details>
https://github.com/llvm/llvm-project/pull/153572
More information about the cfe-commits
mailing list