-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[Clang] Enable constexpr handling for builtin elementwise fshl/fshr #153572
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-llvm-adt @llvm/pr-subscribers-clang Author: Chaitanya Koparkar (ckoparkar) ChangesFixes #153151. Full diff: https://github.com/llvm/llvm-project/pull/153572.diff 6 Files Affected:
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;
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you move the ADT changes to a separate PR and add unit tests?
@kuhar Will do. I'll wait for some reviews on the usage of that ADT change to be sure that it's actually going to be required for this and then create that separate PR. |
APIntOps fshl/r might be cleaner. |
Sounds like the implementation of |
Working on updating this to use the new fshl/r APIntOps. |
f1d86bd
to
aa7d936
Compare
You can test this locally with the following command:git-clang-format --diff HEAD~1 HEAD --extensions cpp -- clang/lib/AST/ByteCode/InterpBuiltin.cpp clang/lib/AST/ExprConstant.cpp clang/test/Sema/constant-builtins-vector.cpp View the diff from clang-format here.diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 651447ffa..c40da9c46 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2857,7 +2857,7 @@ static bool interp__builtin_elementwise_fsh(InterpState &S, CodePtr OpPC,
assert(!Arg1Type->isVectorType());
assert(!Arg2Type->isVectorType());
const APSInt &Shift =
- popToAPSInt(S.Stk, *S.getContext().classify(Arg2Type));
+ 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;
|
aa7d936
to
c8f71ad
Compare
c8f71ad
to
777bc01
Compare
The other PRs this required have been merged, this is ready for review again. |
assert(VecT->getElementType() == | ||
Arg2Type->castAs<VectorType>()->getElementType() && | ||
VecT->getElementType() == | ||
Arg3Type->castAs<VectorType>()->getElementType()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Can you add these assertions in the current interpreter as well?
- I think they aren't exactly correct and will fail if one vector is of type int and the other of type
const int
. However, I'm not sure if that's even possible atm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, let me check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These types are not considered equal much before constexpr evaluation:
typedef const short vector4cshort __attribute__((__vector_size__(8)));
typedef short vector4short __attribute__((__vector_size__(8)));
error: arguments are of different types ('vector4cshort' (vector of 4 'const short' values) vs 'vector4short' (vector of 4 'short' values))
25 | constexpr auto chk = __builtin_elementwise_fshl(x,y,z);
| ^
Do you think I should still add these assertions to the current interpreter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think the assertions still make sense in both cases.
I think we should also use ASTContext::hasSameUnqualifiedType()
in Sema::BuiltinElementwiseTernaryMath()
like we do in BuiltinVectorMath()
.
See also 4d650ef, but that can be a follow-up commit (which will have to adjust the assertions as well).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I didn't clock this behavior as incorrect. But it might be better to resolve this first rather than changing the assertions later? I think the only other ternary op we've made constexpr is fma. Let me file an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created #155405.
247fcd7
to
33807ca
Compare
Fixes #153151.