Skip to content

Commit 79ea319

Browse files
authored
[Clang] Enable constexpr handling for builtin elementwise fshl/fshr (#153572)
Fixes #153151.
1 parent c24e9e3 commit 79ea319

File tree

5 files changed

+170
-4
lines changed

5 files changed

+170
-4
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -875,12 +875,14 @@ of different sizes and signs is forbidden in binary and ternary builtins.
875875
for the comparison.
876876
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
877877
significant bits of the wide value), the combined value is shifted
878-
left by z, and the most significant bits are extracted to produce
878+
left by z (modulo the bit width of the original arguments),
879+
and the most significant bits are extracted to produce
879880
a result that is the same size as the original arguments.
880881

881882
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
882883
significant bits of the wide value), the combined value is shifted
883-
right by z, and the least significant bits are extracted to produce
884+
right by z (modulo the bit width of the original arguments),
885+
and the least significant bits are extracted to produce
884886
a result that is the same size as the original arguments.
885887
T __builtin_elementwise_ctlz(T x[, T y]) return the number of leading 0 bits in the first argument. If integer types
886888
the first argument is 0 and an optional second argument is provided,

clang/include/clang/Basic/Builtins.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,13 +1540,13 @@ def ElementwiseSubSat : Builtin {
15401540

15411541
def ElementwiseFshl : Builtin {
15421542
let Spellings = ["__builtin_elementwise_fshl"];
1543-
let Attributes = [NoThrow, Const, CustomTypeChecking];
1543+
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
15441544
let Prototype = "void(...)";
15451545
}
15461546

15471547
def ElementwiseFshr : Builtin {
15481548
let Spellings = ["__builtin_elementwise_fshr"];
1549-
let Attributes = [NoThrow, Const, CustomTypeChecking];
1549+
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
15501550
let Prototype = "void(...)";
15511551
}
15521552

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2829,6 +2829,65 @@ static bool interp__builtin_select(InterpState &S, CodePtr OpPC,
28292829
return true;
28302830
}
28312831

2832+
static bool interp__builtin_elementwise_fsh(InterpState &S, CodePtr OpPC,
2833+
const CallExpr *Call,
2834+
unsigned BuiltinID) {
2835+
assert(Call->getNumArgs() == 3);
2836+
2837+
QualType Arg0Type = Call->getArg(0)->getType();
2838+
QualType Arg1Type = Call->getArg(1)->getType();
2839+
QualType Arg2Type = Call->getArg(2)->getType();
2840+
2841+
// Non-vector integer types.
2842+
if (!Arg0Type->isVectorType()) {
2843+
const APSInt &Shift =
2844+
popToAPSInt(S.Stk, *S.getContext().classify(Arg2Type));
2845+
const APSInt &Lo = popToAPSInt(S.Stk, *S.getContext().classify(Arg1Type));
2846+
const APSInt &Hi = popToAPSInt(S.Stk, *S.getContext().classify(Arg0Type));
2847+
APSInt Result;
2848+
if (BuiltinID == Builtin::BI__builtin_elementwise_fshl)
2849+
Result = APSInt(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned());
2850+
else if (BuiltinID == Builtin::BI__builtin_elementwise_fshr)
2851+
Result = APSInt(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned());
2852+
else
2853+
llvm_unreachable("Wrong builtin ID");
2854+
pushInteger(S, Result, Call->getType());
2855+
return true;
2856+
}
2857+
2858+
// Vector type.
2859+
const auto *VecT = Arg0Type->castAs<VectorType>();
2860+
const PrimType &ElemT = *S.getContext().classify(VecT->getElementType());
2861+
unsigned NumElems = VecT->getNumElements();
2862+
2863+
const Pointer &VecShift = S.Stk.pop<Pointer>();
2864+
const Pointer &VecLo = S.Stk.pop<Pointer>();
2865+
const Pointer &VecHi = S.Stk.pop<Pointer>();
2866+
const Pointer &Dst = S.Stk.peek<Pointer>();
2867+
for (unsigned I = 0; I != NumElems; ++I) {
2868+
APSInt Hi;
2869+
APSInt Lo;
2870+
APSInt Shift;
2871+
INT_TYPE_SWITCH_NO_BOOL(ElemT, {
2872+
Hi = VecHi.elem<T>(I).toAPSInt();
2873+
Lo = VecLo.elem<T>(I).toAPSInt();
2874+
Shift = VecShift.elem<T>(I).toAPSInt();
2875+
});
2876+
APSInt Result;
2877+
if (BuiltinID == Builtin::BI__builtin_elementwise_fshl)
2878+
Result = APSInt(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned());
2879+
else if (BuiltinID == Builtin::BI__builtin_elementwise_fshr)
2880+
Result = APSInt(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned());
2881+
else
2882+
llvm_unreachable("Wrong builtin ID");
2883+
INT_TYPE_SWITCH_NO_BOOL(ElemT,
2884+
{ Dst.elem<T>(I) = static_cast<T>(Result); });
2885+
}
2886+
Dst.initializeAllElements();
2887+
2888+
return true;
2889+
}
2890+
28322891
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
28332892
uint32_t BuiltinID) {
28342893
if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID))
@@ -3393,6 +3452,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
33933452
case X86::BI__builtin_ia32_selectpd_512:
33943453
return interp__builtin_select(S, OpPC, Call);
33953454

3455+
case Builtin::BI__builtin_elementwise_fshl:
3456+
case Builtin::BI__builtin_elementwise_fshr:
3457+
return interp__builtin_elementwise_fsh(S, OpPC, Call, BuiltinID);
3458+
33963459
default:
33973460
S.FFDiag(S.Current->getLocation(OpPC),
33983461
diag::note_invalid_subexpr_in_const_expr)

clang/lib/AST/ExprConstant.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12108,6 +12108,40 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
1210812108
}
1210912109
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
1211012110
}
12111+
12112+
case Builtin::BI__builtin_elementwise_fshl:
12113+
case Builtin::BI__builtin_elementwise_fshr: {
12114+
APValue SourceHi, SourceLo, SourceShift;
12115+
if (!EvaluateAsRValue(Info, E->getArg(0), SourceHi) ||
12116+
!EvaluateAsRValue(Info, E->getArg(1), SourceLo) ||
12117+
!EvaluateAsRValue(Info, E->getArg(2), SourceShift))
12118+
return false;
12119+
12120+
QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType();
12121+
if (!DestEltTy->isIntegerType())
12122+
return false;
12123+
12124+
unsigned SourceLen = SourceHi.getVectorLength();
12125+
SmallVector<APValue> ResultElements;
12126+
ResultElements.reserve(SourceLen);
12127+
for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) {
12128+
const APSInt &Hi = SourceHi.getVectorElt(EltNum).getInt();
12129+
const APSInt &Lo = SourceLo.getVectorElt(EltNum).getInt();
12130+
const APSInt &Shift = SourceShift.getVectorElt(EltNum).getInt();
12131+
switch (E->getBuiltinCallee()) {
12132+
case Builtin::BI__builtin_elementwise_fshl:
12133+
ResultElements.push_back(APValue(
12134+
APSInt(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned())));
12135+
break;
12136+
case Builtin::BI__builtin_elementwise_fshr:
12137+
ResultElements.push_back(APValue(
12138+
APSInt(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned())));
12139+
break;
12140+
}
12141+
}
12142+
12143+
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
12144+
}
1211112145
}
1211212146
}
1211312147

@@ -14061,6 +14095,25 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1406114095
APInt Result = std::min(LHS, RHS);
1406214096
return Success(APSInt(Result, !LHS.isSigned()), E);
1406314097
}
14098+
case Builtin::BI__builtin_elementwise_fshl:
14099+
case Builtin::BI__builtin_elementwise_fshr: {
14100+
APSInt Hi, Lo, Shift;
14101+
if (!EvaluateInteger(E->getArg(0), Hi, Info) ||
14102+
!EvaluateInteger(E->getArg(1), Lo, Info) ||
14103+
!EvaluateInteger(E->getArg(2), Shift, Info))
14104+
return false;
14105+
14106+
switch (BuiltinOp) {
14107+
case Builtin::BI__builtin_elementwise_fshl: {
14108+
APSInt Result(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned());
14109+
return Success(Result, E);
14110+
}
14111+
case Builtin::BI__builtin_elementwise_fshr: {
14112+
APSInt Result(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned());
14113+
return Success(Result, E);
14114+
}
14115+
}
14116+
}
1406414117
case Builtin::BIstrlen:
1406514118
case Builtin::BIwcslen:
1406614119
// A call to strlen is not a constant expression.

clang/test/Sema/constant-builtins-vector.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,3 +961,51 @@ static_assert(fmaDouble1[3] == 26.0);
961961
constexpr float fmaArray[] = {2.0f, 2.0f, 2.0f, 2.0f};
962962
constexpr float fmaResult = __builtin_elementwise_fma(fmaArray[1], fmaArray[2], fmaArray[3]);
963963
static_assert(fmaResult == 6.0f, "");
964+
965+
static_assert(__builtin_elementwise_fshl((unsigned char)255, (unsigned char)0, (unsigned char)8) == (unsigned char)255);
966+
static_assert(__builtin_elementwise_fshl((char)127, (char)0, (char)8) == (char)127);
967+
static_assert(__builtin_elementwise_fshl((unsigned char)0, (unsigned char)255, (unsigned char)8) == (unsigned char)0);
968+
static_assert(__builtin_elementwise_fshl((char)0, (char)127, (char)8) == (char)0);
969+
static_assert(__builtin_elementwise_fshr((unsigned char)255, (unsigned char)0, (unsigned char)8) == (unsigned char)0);
970+
static_assert(__builtin_elementwise_fshr((char)127, (char)0, (char)8) == (char)0);
971+
static_assert(__builtin_elementwise_fshr((unsigned char)0, (unsigned char)255, (unsigned char)8) == (unsigned char)255);
972+
static_assert(__builtin_elementwise_fshr((char)0, (char)127, (char)8) == (char)127);
973+
static_assert(__builtin_elementwise_fshl((unsigned int)4294967295, (unsigned int)0, (unsigned int)32) == (unsigned int)4294967295);
974+
static_assert(__builtin_elementwise_fshl((int)2147483647, (int)0, (int)32) == (int)2147483647);
975+
static_assert(__builtin_elementwise_fshl((unsigned int)0, (unsigned int)4294967295, (unsigned int)32) == (unsigned int)0);
976+
static_assert(__builtin_elementwise_fshl((int)0, (int)2147483647, (int)32) == (int)0);
977+
static_assert(__builtin_elementwise_fshr((unsigned int)4294967295, (unsigned int)0, (unsigned int)32) == (unsigned int)0);
978+
static_assert(__builtin_elementwise_fshr((int)2147483647, (int)0, (int)32) == (int)0);
979+
static_assert(__builtin_elementwise_fshr((unsigned int)0, (unsigned int)4294967295, (unsigned int)32) == (unsigned int)4294967295);
980+
static_assert(__builtin_elementwise_fshr((int)0, (int)2147483647, (int)32) == (int)2147483647);
981+
static_assert(__builtin_elementwise_fshl((unsigned long long)18446744073709551615ULL, (unsigned long long)0, (unsigned long long)64) == (unsigned long long)18446744073709551615ULL);
982+
static_assert(__builtin_elementwise_fshl((long long)9223372036854775807, (long long)0, (long long)64) == (long long)9223372036854775807);
983+
static_assert(__builtin_elementwise_fshl((unsigned long long)0, (unsigned long long)18446744073709551615ULL, (unsigned long long)64) == (unsigned long long)0);
984+
static_assert(__builtin_elementwise_fshl((long long)0, (long long)9223372036854775807, (long long)64) == (long long)0);
985+
static_assert(__builtin_elementwise_fshr((unsigned long long)18446744073709551615ULL, (unsigned long long)0, (unsigned long long)64) == (unsigned long long)0);
986+
static_assert(__builtin_elementwise_fshr((long long)9223372036854775807, (long long)0, (long long)64) == (long long)0);
987+
static_assert(__builtin_elementwise_fshr((unsigned long long)0, (unsigned long long)18446744073709551615ULL, (unsigned long long)64) == (unsigned long long)18446744073709551615ULL);
988+
static_assert(__builtin_elementwise_fshr((long long)0, (long long)9223372036854775807, (long long)64) == (long long)9223372036854775807);
989+
static_assert(__builtin_elementwise_fshl((short) 1, (short) 2, (short) 3) == (short)8);
990+
static_assert(__builtin_elementwise_fshl((short) 2, (short) 1, (short) 3) == (short)16);
991+
static_assert(__builtin_elementwise_fshl(1, 2 , 2) == 4);
992+
static_assert(__builtin_elementwise_fshl(2L, 1L , 2L) == 8L);
993+
static_assert(__builtin_elementwise_fshr((unsigned char)1, (unsigned char)2, (unsigned char)3) == (unsigned char)32);
994+
constexpr vector4uchar v4s_fshl_var =
995+
__builtin_elementwise_fshl((vector4uchar){255, 15, 0, 2},
996+
(vector4uchar){0, 15, 255, 1},
997+
(vector4uchar){15, 11, 8, 3});
998+
static_assert(v4s_fshl_var[0] == 128);
999+
static_assert(v4s_fshl_var[1] == 120);
1000+
static_assert(v4s_fshl_var[2] == 0);
1001+
static_assert(v4s_fshl_var[3] == 16);
1002+
constexpr vector4uchar v4s_fshr_var =
1003+
__builtin_elementwise_fshr((vector4uchar){255, 15, 0, 1},
1004+
(vector4uchar){0, 15, 255, 2},
1005+
(vector4uchar){15, 11, 8, 3});
1006+
static_assert(v4s_fshr_var[0] == 254);
1007+
static_assert(v4s_fshr_var[1] == 225);
1008+
static_assert(v4s_fshr_var[2] == 255);
1009+
static_assert(v4s_fshr_var[3] == 32);
1010+
static_assert(__builtin_elementwise_fshl(v4s_fshl_var[0], v4s_fshl_var[1], v4s_fshl_var[2]) == 128);
1011+
static_assert(__builtin_elementwise_fshr(v4s_fshr_var[0], v4s_fshr_var[1], v4s_fshr_var[2]) == 253);

0 commit comments

Comments
 (0)