[clang] c8b5b6e - [clang][ExprConst] Reject unary vector shuffles (#158589)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 15 22:58:18 PDT 2025
Author: Timm Baeder
Date: 2025-09-16T07:58:15+02:00
New Revision: c8b5b6e0a61a66a0a09798fdc6c91340a0718f0d
URL: https://github.com/llvm/llvm-project/commit/c8b5b6e0a61a66a0a09798fdc6c91340a0718f0d
DIFF: https://github.com/llvm/llvm-project/commit/c8b5b6e0a61a66a0a09798fdc6c91340a0718f0d.diff
LOG: [clang][ExprConst] Reject unary vector shuffles (#158589)
This is not implemented at compile time and asserts in assertion builds,
so reject it here.
Fixed the coding style in `BuiltinShuffleVector` at the same time.
Fixes #158471
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/AST/ByteCode/Compiler.cpp
clang/lib/AST/ExprConstant.cpp
clang/lib/Sema/SemaChecking.cpp
clang/test/Sema/constant-builtins-vector.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d9fbb21739d69..f184eb1068f76 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -291,6 +291,10 @@ Improvements to Clang's diagnostics
- Fixed a bug where the source location was missing when diagnosing ill-formed
placeholder constraints.
+- The two-element, unary mask variant of ``__builtin_shufflevector`` is now
+ properly being rejected when used at compile-time. It was not implemented
+ and caused assertion failures before (#GH158471).
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index cecdb7b2dda55..c9ce113c4701f 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3986,6 +3986,10 @@ bool Compiler<Emitter>::VisitConvertVectorExpr(const ConvertVectorExpr *E) {
template <class Emitter>
bool Compiler<Emitter>::VisitShuffleVectorExpr(const ShuffleVectorExpr *E) {
+ // FIXME: Unary shuffle with mask not currently supported.
+ if (E->getNumSubExprs() == 2)
+ return this->emitInvalid(E);
+
assert(Initializing);
assert(E->getNumSubExprs() > 2);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 77dc2203576b3..12e4e25bd29c4 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -12187,6 +12187,9 @@ static bool handleVectorShuffle(EvalInfo &Info, const ShuffleVectorExpr *E,
}
bool VectorExprEvaluator::VisitShuffleVectorExpr(const ShuffleVectorExpr *E) {
+ // FIXME: Unary shuffle with mask not currently supported.
+ if (E->getNumSubExprs() == 2)
+ return Error(E);
APValue VecVal1;
const Expr *Vec1 = E->getExpr(0);
if (!EvaluateAsRValue(Info, Vec1, VecVal1))
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index cb8ecb77b73ff..05ee51913aec1 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5558,17 +5558,18 @@ bool Sema::BuiltinComplex(CallExpr *TheCall) {
/// BuiltinShuffleVector - Handle __builtin_shufflevector.
// This is declared to take (...), so we have to check everything.
ExprResult Sema::BuiltinShuffleVector(CallExpr *TheCall) {
- if (TheCall->getNumArgs() < 2)
+ unsigned NumArgs = TheCall->getNumArgs();
+ if (NumArgs < 2)
return ExprError(Diag(TheCall->getEndLoc(),
diag::err_typecheck_call_too_few_args_at_least)
- << 0 /*function call*/ << 2 << TheCall->getNumArgs()
+ << 0 /*function call*/ << 2 << NumArgs
<< /*is non object*/ 0 << TheCall->getSourceRange());
// Determine which of the following types of shufflevector we're checking:
// 1) unary, vector mask: (lhs, mask)
// 2) binary, scalar mask: (lhs, rhs, index, ..., index)
- QualType resType = TheCall->getArg(0)->getType();
- unsigned numElements = 0;
+ QualType ResType = TheCall->getArg(0)->getType();
+ unsigned NumElements = 0;
if (!TheCall->getArg(0)->isTypeDependent() &&
!TheCall->getArg(1)->isTypeDependent()) {
@@ -5578,43 +5579,43 @@ ExprResult Sema::BuiltinShuffleVector(CallExpr *TheCall) {
if (!LHSType->isVectorType() || !RHSType->isVectorType())
return ExprError(
Diag(TheCall->getBeginLoc(), diag::err_vec_builtin_non_vector)
- << TheCall->getDirectCallee() << /*isMorethantwoArgs*/ false
+ << TheCall->getDirectCallee() << /*isMoreThanTwoArgs*/ false
<< SourceRange(TheCall->getArg(0)->getBeginLoc(),
TheCall->getArg(1)->getEndLoc()));
- numElements = LHSType->castAs<VectorType>()->getNumElements();
- unsigned numResElements = TheCall->getNumArgs() - 2;
+ NumElements = LHSType->castAs<VectorType>()->getNumElements();
+ unsigned NumResElements = NumArgs - 2;
// Check to see if we have a call with 2 vector arguments, the unary shuffle
// with mask. If so, verify that RHS is an integer vector type with the
// same number of elts as lhs.
- if (TheCall->getNumArgs() == 2) {
+ if (NumArgs == 2) {
if (!RHSType->hasIntegerRepresentation() ||
- RHSType->castAs<VectorType>()->getNumElements() != numElements)
+ RHSType->castAs<VectorType>()->getNumElements() != NumElements)
return ExprError(Diag(TheCall->getBeginLoc(),
diag::err_vec_builtin_incompatible_vector)
<< TheCall->getDirectCallee()
- << /*isMorethantwoArgs*/ false
+ << /*isMoreThanTwoArgs*/ false
<< SourceRange(TheCall->getArg(1)->getBeginLoc(),
TheCall->getArg(1)->getEndLoc()));
} else if (!Context.hasSameUnqualifiedType(LHSType, RHSType)) {
return ExprError(Diag(TheCall->getBeginLoc(),
diag::err_vec_builtin_incompatible_vector)
<< TheCall->getDirectCallee()
- << /*isMorethantwoArgs*/ false
+ << /*isMoreThanTwoArgs*/ false
<< SourceRange(TheCall->getArg(0)->getBeginLoc(),
TheCall->getArg(1)->getEndLoc()));
- } else if (numElements != numResElements) {
- QualType eltType = LHSType->castAs<VectorType>()->getElementType();
- resType = resType->isExtVectorType()
- ? Context.getExtVectorType(eltType, numResElements)
- : Context.getVectorType(eltType, numResElements,
+ } else if (NumElements != NumResElements) {
+ QualType EltType = LHSType->castAs<VectorType>()->getElementType();
+ ResType = ResType->isExtVectorType()
+ ? Context.getExtVectorType(EltType, NumResElements)
+ : Context.getVectorType(EltType, NumResElements,
VectorKind::Generic);
}
}
- for (unsigned i = 2; i < TheCall->getNumArgs(); i++) {
- Expr *Arg = TheCall->getArg(i);
+ for (unsigned I = 2; I != NumArgs; ++I) {
+ Expr *Arg = TheCall->getArg(I);
if (Arg->isTypeDependent() || Arg->isValueDependent())
continue;
@@ -5628,23 +5629,21 @@ ExprResult Sema::BuiltinShuffleVector(CallExpr *TheCall) {
if (Result->isSigned() && Result->isAllOnes())
;
else if (Result->getActiveBits() > 64 ||
- Result->getZExtValue() >= numElements * 2)
+ Result->getZExtValue() >= NumElements * 2)
return ExprError(Diag(TheCall->getBeginLoc(),
diag::err_shufflevector_argument_too_large)
<< Arg->getSourceRange());
- TheCall->setArg(i, ConstantExpr::Create(Context, Arg, APValue(*Result)));
+ TheCall->setArg(I, ConstantExpr::Create(Context, Arg, APValue(*Result)));
}
- SmallVector<Expr *> exprs;
- for (unsigned i = 0, e = TheCall->getNumArgs(); i != e; i++) {
- exprs.push_back(TheCall->getArg(i));
- TheCall->setArg(i, nullptr);
- }
+ auto *Result = new (Context) ShuffleVectorExpr(
+ Context, ArrayRef(TheCall->getArgs(), NumArgs), ResType,
+ TheCall->getCallee()->getBeginLoc(), TheCall->getRParenLoc());
- return new (Context) ShuffleVectorExpr(Context, exprs, resType,
- TheCall->getCallee()->getBeginLoc(),
- TheCall->getRParenLoc());
+ // All moved to Result.
+ TheCall->shrinkNumArgs(0);
+ return Result;
}
ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
diff --git a/clang/test/Sema/constant-builtins-vector.cpp b/clang/test/Sema/constant-builtins-vector.cpp
index 714a7fb753214..455284ef65e9b 100644
--- a/clang/test/Sema/constant-builtins-vector.cpp
+++ b/clang/test/Sema/constant-builtins-vector.cpp
@@ -731,6 +731,19 @@ permitted in a constexpr context}}
vector4charConst1,
vector4charConst2, -1, -1, -1, -1);
+namespace UnaryShuffleUnsupported {
+ typedef int vi6 __attribute__((ext_vector_type(2)));
+ constexpr int foo() { // expected-error {{never produces a constant expression}}
+ vi6 a = {1,2};
+ vi6 b = {3,4};
+ vi6 r = __builtin_shufflevector(a, b); // expected-note 2{{subexpression not valid in a constant expression}}
+
+ return r[0] + r[1];
+ }
+ static_assert(foo() == 0); // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to}}
+}
+
static_assert(__builtin_reduce_add((vector4char){}) == 0);
static_assert(__builtin_reduce_add((vector4char){1, 2, 3, 4}) == 10);
static_assert(__builtin_reduce_add((vector4short){10, 20, 30, 40}) == 100);
More information about the cfe-commits
mailing list