Skip to content

Commit c14783d

Browse files
[clang] Constexpr for __builtin_shufflevector and __builtin_convertvector
Summary: This patch adds constexpr support for __builtin_shufflevector and __builtin_convertvector. A small oddity encountered was that the arg to the intrinsics may be an lvalue without any sort of implicit cast of any kind. I solved this through the EvaluateVectorOrLValue function, which treats the lvalue as if it was in an rvalue cast, which gets me the desired vector.
1 parent 95e668f commit c14783d

File tree

4 files changed

+204
-3
lines changed

4 files changed

+204
-3
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2927,7 +2927,7 @@ Query for this feature with ``__has_builtin(__builtin_dump_struct)``
29272927
``__builtin_shufflevector`` is used to express generic vector
29282928
permutation/shuffle/swizzle operations. This builtin is also very important
29292929
for the implementation of various target-specific header files like
2930-
``<xmmintrin.h>``.
2930+
``<xmmintrin.h>``. This builtin can be used within constant expressions.
29312931
29322932
**Syntax**:
29332933
@@ -2981,7 +2981,8 @@ Query for this feature with ``__has_builtin(__builtin_shufflevector)``.
29812981
29822982
``__builtin_convertvector`` is used to express generic vector
29832983
type-conversion operations. The input vector and the output vector
2984-
type must have the same number of elements.
2984+
type must have the same number of elements. This builtin can be used within
2985+
constant expressions.
29852986
29862987
**Syntax**:
29872988

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ Non-comprehensive list of changes in this release
203203
- ``__typeof_unqual__`` is available in all C modes as an extension, which behaves
204204
like ``typeof_unqual`` from C23, similar to ``__typeof__`` and ``typeof``.
205205

206+
- Builtins ``__builtin_shufflevector()`` and ``__builtin_convertvector()`` may
207+
now be used within constant expressions.
208+
206209
New Compiler Flags
207210
------------------
208211
- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and

clang/lib/AST/ExprConstant.cpp

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2706,7 +2706,8 @@ static bool checkFloatingPointResult(EvalInfo &Info, const Expr *E,
27062706
static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E,
27072707
QualType SrcType, QualType DestType,
27082708
APFloat &Result) {
2709-
assert(isa<CastExpr>(E) || isa<CompoundAssignOperator>(E));
2709+
assert(isa<CastExpr>(E) || isa<CompoundAssignOperator>(E) ||
2710+
isa<ConvertVectorExpr>(E));
27102711
llvm::RoundingMode RM = getActiveRoundingMode(Info, E);
27112712
APFloat::opStatus St;
27122713
APFloat Value = Result;
@@ -10709,6 +10710,9 @@ namespace {
1070910710
bool VisitUnaryImag(const UnaryOperator *E);
1071010711
bool VisitBinaryOperator(const BinaryOperator *E);
1071110712
bool VisitUnaryOperator(const UnaryOperator *E);
10713+
bool VisitConvertVectorExpr(const ConvertVectorExpr *E);
10714+
bool VisitShuffleVectorExpr(const ShuffleVectorExpr *E);
10715+
1071210716
// FIXME: Missing: conditional operator (for GNU
1071310717
// conditional select), shufflevector, ExtVectorElementExpr
1071410718
};
@@ -10961,6 +10965,138 @@ bool VectorExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
1096110965
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
1096210966
}
1096310967

10968+
static bool EvaluateVectorOrLValue(APValue &Result, EvalInfo &Info,
10969+
const Expr *E, const QualType &Type) {
10970+
if (!Evaluate(Result, Info, E))
10971+
return false;
10972+
10973+
if (Result.isLValue()) {
10974+
// Source of the data is an lvalue; Manually handle the lvalue as if
10975+
// it was an rvalue to get the current APValue.
10976+
LValue LValueFound;
10977+
LValueFound.setFrom(Info.Ctx, Result);
10978+
if (!handleLValueToRValueConversion(Info, E, Type, LValueFound, Result)) {
10979+
return false;
10980+
}
10981+
}
10982+
10983+
if (!Result.isVector()) {
10984+
return false;
10985+
}
10986+
return true;
10987+
}
10988+
10989+
static bool handleVectorConversion(EvalInfo &Info, const FPOptions FPO,
10990+
const Expr *E, QualType SourceTy,
10991+
QualType DestTy, APValue const &Original,
10992+
APValue &Result) {
10993+
if (SourceTy->isIntegerType()) {
10994+
if (DestTy->isRealFloatingType()) {
10995+
Result = APValue(APFloat(0.0));
10996+
return HandleIntToFloatCast(Info, E, FPO, SourceTy, Original.getInt(),
10997+
DestTy, Result.getFloat());
10998+
}
10999+
if (DestTy->isIntegerType()) {
11000+
Result = APValue(
11001+
HandleIntToIntCast(Info, E, DestTy, SourceTy, Original.getInt()));
11002+
return true;
11003+
}
11004+
} else if (SourceTy->isRealFloatingType()) {
11005+
if (DestTy->isRealFloatingType()) {
11006+
Result = Original;
11007+
return HandleFloatToFloatCast(Info, E, SourceTy, DestTy,
11008+
Result.getFloat());
11009+
}
11010+
if (DestTy->isIntegerType()) {
11011+
Result = APValue(APSInt());
11012+
return HandleFloatToIntCast(Info, E, SourceTy, Original.getFloat(),
11013+
DestTy, Result.getInt());
11014+
}
11015+
}
11016+
return false;
11017+
}
11018+
11019+
bool VectorExprEvaluator::VisitConvertVectorExpr(const ConvertVectorExpr *E) {
11020+
APValue Source;
11021+
QualType SourceVecType = E->getSrcExpr()->getType();
11022+
if (!EvaluateVectorOrLValue(Source, Info, E->getSrcExpr(), SourceVecType))
11023+
return false;
11024+
11025+
QualType DestTy = E->getType()->castAs<VectorType>()->getElementType();
11026+
QualType SourceTy = SourceVecType->castAs<VectorType>()->getElementType();
11027+
11028+
const FPOptions FPO = E->getFPFeaturesInEffect(Info.Ctx.getLangOpts());
11029+
11030+
SmallVector<APValue, 4> ResultElements;
11031+
ResultElements.reserve(Source.getVectorLength());
11032+
for (unsigned EltNum = 0; EltNum < Source.getVectorLength(); ++EltNum) {
11033+
APValue Elt;
11034+
if (!handleVectorConversion(Info, FPO, E, SourceTy, DestTy,
11035+
Source.getVectorElt(EltNum), Elt))
11036+
return false;
11037+
ResultElements.push_back(std::move(Elt));
11038+
}
11039+
11040+
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
11041+
}
11042+
11043+
static bool handleVectorShuffle(EvalInfo &Info, const ShuffleVectorExpr *E,
11044+
QualType ElemType, APValue const &VecVal1,
11045+
APValue const &VecVal2, unsigned EltNum,
11046+
APValue &Result) {
11047+
unsigned const TotalElementsInAVector = VecVal1.getVectorLength();
11048+
11049+
Expr const *IndexExpr = E->getExpr(2 + EltNum);
11050+
APSInt IndexVal;
11051+
if (!EvaluateInteger(IndexExpr, IndexVal, Info)) {
11052+
return false;
11053+
}
11054+
11055+
uint32_t index = IndexVal.getZExtValue();
11056+
// The spec says that -1 should be treated as undef for optimizations,
11057+
// but in constexpr we need to choose a value. We'll choose 0.
11058+
if (index >= TotalElementsInAVector * 2) {
11059+
index = 0;
11060+
}
11061+
11062+
if (index >= TotalElementsInAVector) {
11063+
Result = VecVal2.getVectorElt(index - TotalElementsInAVector);
11064+
} else {
11065+
Result = VecVal1.getVectorElt(index);
11066+
}
11067+
return true;
11068+
}
11069+
11070+
bool VectorExprEvaluator::VisitShuffleVectorExpr(const ShuffleVectorExpr *E) {
11071+
APValue VecVal1;
11072+
const Expr *Vec1 = E->getExpr(0);
11073+
if (!EvaluateVectorOrLValue(VecVal1, Info, Vec1, Vec1->getType()))
11074+
return false;
11075+
APValue VecVal2;
11076+
const Expr *Vec2 = E->getExpr(1);
11077+
if (!EvaluateVectorOrLValue(VecVal2, Info, Vec2, Vec2->getType()))
11078+
return false;
11079+
11080+
VectorType const *DestVecTy = E->getType()->castAs<VectorType>();
11081+
if (!DestVecTy) {
11082+
return false;
11083+
}
11084+
QualType DestElTy = DestVecTy->getElementType();
11085+
11086+
auto TotalElementsInOutputVector = DestVecTy->getNumElements();
11087+
11088+
SmallVector<APValue, 4> ResultElements;
11089+
ResultElements.reserve(TotalElementsInOutputVector);
11090+
for (unsigned EltNum = 0; EltNum < TotalElementsInOutputVector; ++EltNum) {
11091+
APValue Elt;
11092+
if (!handleVectorShuffle(Info, E, DestElTy, VecVal1, VecVal2, EltNum, Elt))
11093+
return false;
11094+
ResultElements.push_back(std::move(Elt));
11095+
}
11096+
11097+
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
11098+
}
11099+
1096411100
//===----------------------------------------------------------------------===//
1096511101
// Array Evaluation
1096611102
//===----------------------------------------------------------------------===//

clang/test/Sema/constant-builtins-2.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,3 +427,64 @@ extern __typeof__(__builtin_expect(0, 0)) bi0;
427427
// Strings
428428
int array1[__builtin_strlen("ab\0cd")];
429429
int array2[(sizeof(array1)/sizeof(int)) == 2? 1 : -1];
430+
431+
typedef double vector4double __attribute__((__vector_size__(32)));
432+
typedef float vector4float __attribute__((__vector_size__(16)));
433+
typedef long long vector4long __attribute__((__vector_size__(32)));
434+
typedef int vector4int __attribute__((__vector_size__(16)));
435+
typedef short vector4short __attribute__((__vector_size__(8)));
436+
typedef char vector4char __attribute__((__vector_size__(4)));
437+
typedef double vector8double __attribute__((__vector_size__(64)));
438+
typedef float vector8float __attribute__((__vector_size__(32)));
439+
typedef long long vector8long __attribute__((__vector_size__(64)));
440+
typedef int vector8int __attribute__((__vector_size__(32)));
441+
typedef short vector8short __attribute__((__vector_size__(16)));
442+
typedef char vector8char __attribute__((__vector_size__(8)));
443+
444+
// Convert vector
445+
#define CHECK_NUM(__size, __typeFrom, __typeTo, ...) \
446+
vector##__size##__typeTo \
447+
from_##vector##__size##__typeFrom##_to_##vector##__size##__typeTo##_var = \
448+
__builtin_convertvector((vector##__size##__typeFrom){__VA_ARGS__}, \
449+
vector##__size##__typeTo);
450+
#define CHECK_TO_ALL_TYPES(__size, __typeFrom, ...) \
451+
CHECK_NUM(__size, __typeFrom, double, __VA_ARGS__) \
452+
CHECK_NUM(__size, __typeFrom, float, __VA_ARGS__) \
453+
CHECK_NUM(__size, __typeFrom, long, __VA_ARGS__) \
454+
CHECK_NUM(__size, __typeFrom, int, __VA_ARGS__) \
455+
CHECK_NUM(__size, __typeFrom, short, __VA_ARGS__) \
456+
CHECK_NUM(__size, __typeFrom, char, __VA_ARGS__)
457+
458+
#define CHECK_ALL_COMBINATIONS(__size, ...) \
459+
CHECK_TO_ALL_TYPES(__size, double, __VA_ARGS__) \
460+
CHECK_TO_ALL_TYPES(__size, float, __VA_ARGS__) \
461+
CHECK_TO_ALL_TYPES(__size, long, __VA_ARGS__) \
462+
CHECK_TO_ALL_TYPES(__size, int, __VA_ARGS__) \
463+
CHECK_TO_ALL_TYPES(__size, short, __VA_ARGS__) \
464+
CHECK_TO_ALL_TYPES(__size, char, __VA_ARGS__)
465+
466+
CHECK_ALL_COMBINATIONS(4, 0, 1, 2, 3);
467+
CHECK_ALL_COMBINATIONS(8, 0, 1, 2, 3, 4, 5, 6, 7);
468+
#undef CHECK_ALL_COMBINATIONS
469+
#undef CHECK_TO_ALL_TYPES
470+
#undef CHECK_NUM
471+
472+
// Shuffle vector
473+
vector4int const vector4intConst1 = {0, 1, 2, 3};
474+
vector4int const vector4intConst2 = {4, 5, 6, 7};
475+
vector8int const vector8intConst = {};
476+
477+
vector4int vectorShuffle1 =
478+
__builtin_shufflevector(vector4intConst1, vector4intConst2, 0, 1, 2, 3);
479+
vector4int vectorShuffle2 =
480+
__builtin_shufflevector(vector4intConst1, vector4intConst2, 4, 5, 6, 7);
481+
vector4int vectorShuffle3 =
482+
__builtin_shufflevector(vector4intConst1, vector4intConst2, -1, -1, -1, -1);
483+
vector4int vectorShuffle4 =
484+
__builtin_shufflevector(vector4intConst1, vector4intConst2, 0, 2, 4, 6);
485+
vector8int vectorShuffle5 = __builtin_shufflevector(
486+
vector8intConst, vector8intConst, 0, 2, 4, 6, 8, 10, 12, 14);
487+
vector4int vectorShuffle6 = __builtin_shufflevector(
488+
vector8intConst, vector8intConst, 0, 2, 4, 6);
489+
vector8int vectorShuffle7 =
490+
__builtin_shufflevector(vector4intConst1, vector4intConst2, 0, 2, 4, 6, 1, 3, 5, 7);

0 commit comments

Comments
 (0)