Skip to content

Commit 7753429

Browse files
authored
[clang][ExprConst] allow single element access of vector object to be constant expression (#101126)
This is a slightly updated version of #72607, originally authored by @yuanfang-chen
1 parent 269cefb commit 7753429

File tree

8 files changed

+215
-65
lines changed

8 files changed

+215
-65
lines changed

clang/docs/ReleaseNotes.rst

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ sections with improvements to Clang's support for those languages.
7575

7676
C++ Language Changes
7777
--------------------
78+
- Allow single element access of GCC vector/ext_vector_type object to be
79+
constant expression. Supports the `V.xyzw` syntax and other tidbits
80+
as seen in OpenCL. Selecting multiple elements is left as a future work.
7881

7982
C++17 Feature Support
8083
^^^^^^^^^^^^^^^^^^^^^

clang/lib/AST/ExprConstant.cpp

+106-4
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ namespace {
222222
ArraySize = 2;
223223
MostDerivedLength = I + 1;
224224
IsArray = true;
225+
} else if (const auto *VT = Type->getAs<VectorType>()) {
226+
Type = VT->getElementType();
227+
ArraySize = VT->getNumElements();
228+
MostDerivedLength = I + 1;
229+
IsArray = true;
225230
} else if (const FieldDecl *FD = getAsField(Path[I])) {
226231
Type = FD->getType();
227232
ArraySize = 0;
@@ -268,7 +273,6 @@ namespace {
268273
/// If the current array is an unsized array, the value of this is
269274
/// undefined.
270275
uint64_t MostDerivedArraySize;
271-
272276
/// The type of the most derived object referred to by this address.
273277
QualType MostDerivedType;
274278

@@ -442,6 +446,16 @@ namespace {
442446
MostDerivedArraySize = 2;
443447
MostDerivedPathLength = Entries.size();
444448
}
449+
450+
void addVectorElementUnchecked(QualType EltTy, uint64_t Size,
451+
uint64_t Idx) {
452+
Entries.push_back(PathEntry::ArrayIndex(Idx));
453+
MostDerivedType = EltTy;
454+
MostDerivedPathLength = Entries.size();
455+
MostDerivedArraySize = 0;
456+
MostDerivedIsArrayElement = false;
457+
}
458+
445459
void diagnoseUnsizedArrayPointerArithmetic(EvalInfo &Info, const Expr *E);
446460
void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E,
447461
const APSInt &N);
@@ -1737,6 +1751,11 @@ namespace {
17371751
if (checkSubobject(Info, E, Imag ? CSK_Imag : CSK_Real))
17381752
Designator.addComplexUnchecked(EltTy, Imag);
17391753
}
1754+
void addVectorElement(EvalInfo &Info, const Expr *E, QualType EltTy,
1755+
uint64_t Size, uint64_t Idx) {
1756+
if (checkSubobject(Info, E, CSK_VectorElement))
1757+
Designator.addVectorElementUnchecked(EltTy, Size, Idx);
1758+
}
17401759
void clearIsNullPointer() {
17411760
IsNullPtr = false;
17421761
}
@@ -3310,6 +3329,19 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E,
33103329
return true;
33113330
}
33123331

3332+
static bool HandleLValueVectorElement(EvalInfo &Info, const Expr *E,
3333+
LValue &LVal, QualType EltTy,
3334+
uint64_t Size, uint64_t Idx) {
3335+
if (Idx) {
3336+
CharUnits SizeOfElement;
3337+
if (!HandleSizeof(Info, E->getExprLoc(), EltTy, SizeOfElement))
3338+
return false;
3339+
LVal.Offset += SizeOfElement * Idx;
3340+
}
3341+
LVal.addVectorElement(Info, E, EltTy, Size, Idx);
3342+
return true;
3343+
}
3344+
33133345
/// Try to evaluate the initializer for a variable declaration.
33143346
///
33153347
/// \param Info Information about the ongoing evaluation.
@@ -3855,6 +3887,27 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
38553887
return handler.found(Index ? O->getComplexFloatImag()
38563888
: O->getComplexFloatReal(), ObjType);
38573889
}
3890+
} else if (const auto *VT = ObjType->getAs<VectorType>()) {
3891+
uint64_t Index = Sub.Entries[I].getAsArrayIndex();
3892+
unsigned NumElements = VT->getNumElements();
3893+
if (Index == NumElements) {
3894+
if (Info.getLangOpts().CPlusPlus11)
3895+
Info.FFDiag(E, diag::note_constexpr_access_past_end)
3896+
<< handler.AccessKind;
3897+
else
3898+
Info.FFDiag(E);
3899+
return handler.failed();
3900+
}
3901+
3902+
if (Index > NumElements) {
3903+
Info.CCEDiag(E, diag::note_constexpr_array_index)
3904+
<< Index << /*array*/ 0 << NumElements;
3905+
return handler.failed();
3906+
}
3907+
3908+
ObjType = VT->getElementType();
3909+
assert(I == N - 1 && "extracting subobject of scalar?");
3910+
return handler.found(O->getVectorElt(Index), ObjType);
38583911
} else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
38593912
if (Field->isMutable() &&
38603913
!Obj.mayAccessMutableMembers(Info, handler.AccessKind)) {
@@ -8509,6 +8562,7 @@ class LValueExprEvaluator
85098562
bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
85108563
bool VisitCXXUuidofExpr(const CXXUuidofExpr *E);
85118564
bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
8565+
bool VisitExtVectorElementExpr(const ExtVectorElementExpr *E);
85128566
bool VisitUnaryDeref(const UnaryOperator *E);
85138567
bool VisitUnaryReal(const UnaryOperator *E);
85148568
bool VisitUnaryImag(const UnaryOperator *E);
@@ -8850,15 +8904,63 @@ bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {
88508904
return LValueExprEvaluatorBaseTy::VisitMemberExpr(E);
88518905
}
88528906

8907+
bool LValueExprEvaluator::VisitExtVectorElementExpr(
8908+
const ExtVectorElementExpr *E) {
8909+
bool Success = true;
8910+
8911+
APValue Val;
8912+
if (!Evaluate(Val, Info, E->getBase())) {
8913+
if (!Info.noteFailure())
8914+
return false;
8915+
Success = false;
8916+
}
8917+
8918+
SmallVector<uint32_t, 4> Indices;
8919+
E->getEncodedElementAccess(Indices);
8920+
// FIXME: support accessing more than one element
8921+
if (Indices.size() > 1)
8922+
return false;
8923+
8924+
if (Success) {
8925+
Result.setFrom(Info.Ctx, Val);
8926+
const auto *VT = E->getBase()->getType()->castAs<VectorType>();
8927+
HandleLValueVectorElement(Info, E, Result, VT->getElementType(),
8928+
VT->getNumElements(), Indices[0]);
8929+
}
8930+
8931+
return Success;
8932+
}
8933+
88538934
bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
8854-
// FIXME: Deal with vectors as array subscript bases.
8855-
if (E->getBase()->getType()->isVectorType() ||
8856-
E->getBase()->getType()->isSveVLSBuiltinType())
8935+
if (E->getBase()->getType()->isSveVLSBuiltinType())
88578936
return Error(E);
88588937

88598938
APSInt Index;
88608939
bool Success = true;
88618940

8941+
if (const auto *VT = E->getBase()->getType()->getAs<VectorType>()) {
8942+
APValue Val;
8943+
if (!Evaluate(Val, Info, E->getBase())) {
8944+
if (!Info.noteFailure())
8945+
return false;
8946+
Success = false;
8947+
}
8948+
8949+
if (!EvaluateInteger(E->getIdx(), Index, Info)) {
8950+
if (!Info.noteFailure())
8951+
return false;
8952+
Success = false;
8953+
}
8954+
8955+
if (Success) {
8956+
Result.setFrom(Info.Ctx, Val);
8957+
HandleLValueVectorElement(Info, E, Result, VT->getElementType(),
8958+
VT->getNumElements(), Index.getExtValue());
8959+
}
8960+
8961+
return Success;
8962+
}
8963+
88628964
// C++17's rules require us to evaluate the LHS first, regardless of which
88638965
// side is the base.
88648966
for (const Expr *SubExpr : {E->getLHS(), E->getRHS()}) {

clang/lib/AST/Interp/State.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ enum CheckSubobjectKind {
4444
CSK_ArrayToPointer,
4545
CSK_ArrayIndex,
4646
CSK_Real,
47-
CSK_Imag
47+
CSK_Imag,
48+
CSK_VectorElement
4849
};
4950

5051
namespace interp {

clang/test/AST/Interp/arrays.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ static_assert(*(&arr[0]) == 1, "");
114114
static_assert(*(&arr[1]) == 2, "");
115115

116116
constexpr const int *OOB = (arr + 3) - 3; // both-error {{must be initialized by a constant expression}} \
117-
// both-note {{cannot refer to element 3 of array of 2}}
117+
// both-note {{cannot refer to element 3 of array of 2 elements}}
118118

119119
template<typename T>
120120
constexpr T getElementOf(T* array, int i) {

clang/test/AST/Interp/builtin-functions.cpp

+13-13
Original file line numberDiff line numberDiff line change
@@ -866,11 +866,11 @@ namespace convertvector {
866866
constexpr vector8BitInt128 from_vector8BitInt128_to_vector8BitInt128_var =
867867
__builtin_convertvector((vector8BitInt128){0, 1, 2, 3, 4, 5, 6, 7},
868868
vector8BitInt128);
869-
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[0] == 0, ""); // ref-error {{not an integral constant expression}}
870-
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[1] == 1, ""); // ref-error {{not an integral constant expression}}
871-
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[2] == 2, ""); // ref-error {{not an integral constant expression}}
872-
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[3] == 3, ""); // ref-error {{not an integral constant expression}}
873-
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[4] == 4, ""); // ref-error {{not an integral constant expression}}
869+
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[0] == 0, "");
870+
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[1] == 1, "");
871+
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[2] == 2, "");
872+
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[3] == 3, "");
873+
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[4] == 4, "");
874874
}
875875

876876
namespace shufflevector {
@@ -890,14 +890,14 @@ namespace shufflevector {
890890
constexpr vector8char vectorShuffle6 = __builtin_shufflevector(
891891
vector4charConst1, vector4charConst2, 0, 2, 4, 6, 1, 3, 5, 7);
892892

893-
static_assert(vectorShuffle6[0] == 0, "");// ref-error {{not an integral constant expression}}
894-
static_assert(vectorShuffle6[1] == 2, "");// ref-error {{not an integral constant expression}}
895-
static_assert(vectorShuffle6[2] == 4, "");// ref-error {{not an integral constant expression}}
896-
static_assert(vectorShuffle6[3] == 6, "");// ref-error {{not an integral constant expression}}
897-
static_assert(vectorShuffle6[4] == 1, "");// ref-error {{not an integral constant expression}}
898-
static_assert(vectorShuffle6[5] == 3, "");// ref-error {{not an integral constant expression}}
899-
static_assert(vectorShuffle6[6] == 5, "");// ref-error {{not an integral constant expression}}
900-
static_assert(vectorShuffle6[7] == 7, "");// ref-error {{not an integral constant expression}}
893+
static_assert(vectorShuffle6[0] == 0, "");
894+
static_assert(vectorShuffle6[1] == 2, "");
895+
static_assert(vectorShuffle6[2] == 4, "");
896+
static_assert(vectorShuffle6[3] == 6, "");
897+
static_assert(vectorShuffle6[4] == 1, "");
898+
static_assert(vectorShuffle6[5] == 3, "");
899+
static_assert(vectorShuffle6[6] == 5, "");
900+
static_assert(vectorShuffle6[7] == 7, "");
901901

902902
constexpr vector4char vectorShuffleFail1 = __builtin_shufflevector( // both-error {{must be initialized by a constant expression}}\
903903
// ref-error {{index for __builtin_shufflevector not within the bounds of the input vectors; index of -1 found at position 0 is not permitted in a constexpr context}}

clang/test/AST/Interp/vectors.cpp

+24-25
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,39 @@
33

44
typedef int __attribute__((vector_size(16))) VI4;
55
constexpr VI4 A = {1,2,3,4};
6-
static_assert(A[0] == 1, ""); // ref-error {{not an integral constant expression}}
7-
static_assert(A[1] == 2, ""); // ref-error {{not an integral constant expression}}
8-
static_assert(A[2] == 3, ""); // ref-error {{not an integral constant expression}}
9-
static_assert(A[3] == 4, ""); // ref-error {{not an integral constant expression}}
6+
static_assert(A[0] == 1, "");
7+
static_assert(A[1] == 2, "");
8+
static_assert(A[2] == 3, "");
9+
static_assert(A[3] == 4, "");
1010

1111

1212
/// FIXME: It would be nice if the note said 'vector' instead of 'array'.
13-
static_assert(A[12] == 4, ""); // ref-error {{not an integral constant expression}} \
14-
// expected-error {{not an integral constant expression}} \
15-
// expected-note {{cannot refer to element 12 of array of 4 elements in a constant expression}}
13+
static_assert(A[12] == 4, ""); // both-error {{not an integral constant expression}} \
14+
// both-note {{cannot refer to element 12 of array of 4 elements in a constant expression}}
1615

1716

1817
/// VectorSplat casts
1918
typedef __attribute__(( ext_vector_type(4) )) float float4;
2019
constexpr float4 vec4_0 = (float4)0.5f;
21-
static_assert(vec4_0[0] == 0.5, ""); // ref-error {{not an integral constant expression}}
22-
static_assert(vec4_0[1] == 0.5, ""); // ref-error {{not an integral constant expression}}
23-
static_assert(vec4_0[2] == 0.5, ""); // ref-error {{not an integral constant expression}}
24-
static_assert(vec4_0[3] == 0.5, ""); // ref-error {{not an integral constant expression}}
20+
static_assert(vec4_0[0] == 0.5, "");
21+
static_assert(vec4_0[1] == 0.5, "");
22+
static_assert(vec4_0[2] == 0.5, "");
23+
static_assert(vec4_0[3] == 0.5, "");
2524
constexpr int vec4_0_discarded = ((float4)12.0f, 0);
2625

2726

2827
/// ImplicitValueInitExpr of vector type
2928
constexpr float4 arr4[2] = {
3029
{1,2,3,4},
3130
};
32-
static_assert(arr4[0][0] == 1, ""); // ref-error {{not an integral constant expression}}
33-
static_assert(arr4[0][1] == 2, ""); // ref-error {{not an integral constant expression}}
34-
static_assert(arr4[0][2] == 3, ""); // ref-error {{not an integral constant expression}}
35-
static_assert(arr4[0][3] == 4, ""); // ref-error {{not an integral constant expression}}
36-
static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
37-
static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
38-
static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
39-
static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
31+
static_assert(arr4[0][0] == 1, "");
32+
static_assert(arr4[0][1] == 2, "");
33+
static_assert(arr4[0][2] == 3, "");
34+
static_assert(arr4[0][3] == 4, "");
35+
static_assert(arr4[1][0] == 0, "");
36+
static_assert(arr4[1][0] == 0, "");
37+
static_assert(arr4[1][0] == 0, "");
38+
static_assert(arr4[1][0] == 0, "");
4039

4140

4241
/// From constant-expression-cxx11.cpp
@@ -65,10 +64,10 @@ namespace {
6564
namespace BoolToSignedIntegralCast{
6665
typedef __attribute__((__ext_vector_type__(4))) unsigned int int4;
6766
constexpr int4 intsT = (int4)true;
68-
static_assert(intsT[0] == -1, "");// ref-error {{not an integral constant expression}}
69-
static_assert(intsT[1] == -1, "");// ref-error {{not an integral constant expression}}
70-
static_assert(intsT[2] == -1, "");// ref-error {{not an integral constant expression}}
71-
static_assert(intsT[3] == -1, "");// ref-error {{not an integral constant expression}}
67+
static_assert(intsT[0] == -1, "");
68+
static_assert(intsT[1] == -1, "");
69+
static_assert(intsT[2] == -1, "");
70+
static_assert(intsT[3] == -1, "");
7271
}
7372

7473
namespace VectorElementExpr {
@@ -78,8 +77,8 @@ namespace VectorElementExpr {
7877
static_assert(oneElt == 3);
7978

8079
constexpr int2 twoElts = ((int4){11, 22, 33, 44}).yz;
81-
static_assert(twoElts.x == 22, ""); // ref-error {{not an integral constant expression}}
82-
static_assert(twoElts.y == 33, ""); // ref-error {{not an integral constant expression}}
80+
static_assert(twoElts.x == 22, "");
81+
static_assert(twoElts.y == 33, "");
8382
}
8483

8584
namespace Temporaries {

clang/test/CodeGenCXX/temporaries.cpp

+20-21
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,26 @@ namespace RefTempSubobject {
6464
constexpr const SelfReferential &sr = SelfReferential();
6565
}
6666

67+
namespace Vector {
68+
typedef __attribute__((vector_size(16))) int vi4a;
69+
typedef __attribute__((ext_vector_type(4))) int vi4b;
70+
struct S {
71+
vi4a v;
72+
vi4b w;
73+
};
74+
75+
int &&r = S().v[1];
76+
// CHECK: @_ZGRN6Vector1rE_ = internal global i32 0, align 4
77+
// CHECK: @_ZN6Vector1rE = constant ptr @_ZGRN6Vector1rE_, align 8
78+
79+
int &&s = S().w[1];
80+
// CHECK: @_ZGRN6Vector1sE_ = internal global i32 0, align 4
81+
// CHECK: @_ZN6Vector1sE = constant ptr @_ZGRN6Vector1sE_, align 8
82+
83+
int &&t = S().w.y;
84+
// CHECK: @_ZGRN6Vector1tE_ = internal global i32 0, align 4
85+
// CHECK: @_ZN6Vector1tE = constant ptr @_ZGRN6Vector1tE_, align 8
86+
}
6787
struct A {
6888
A();
6989
~A();
@@ -665,27 +685,6 @@ namespace Bitfield {
665685
int &&r = S().a;
666686
}
667687

668-
namespace Vector {
669-
typedef __attribute__((vector_size(16))) int vi4a;
670-
typedef __attribute__((ext_vector_type(4))) int vi4b;
671-
struct S {
672-
vi4a v;
673-
vi4b w;
674-
};
675-
// CHECK: alloca
676-
// CHECK: extractelement
677-
// CHECK: store i32 {{.*}}, ptr @_ZGRN6Vector1rE_
678-
// CHECK: store ptr @_ZGRN6Vector1rE_, ptr @_ZN6Vector1rE,
679-
int &&r = S().v[1];
680-
681-
// CHECK: alloca
682-
// CHECK: extractelement
683-
// CHECK: store i32 {{.*}}, ptr @_ZGRN6Vector1sE_
684-
// CHECK: store ptr @_ZGRN6Vector1sE_, ptr @_ZN6Vector1sE,
685-
int &&s = S().w[1];
686-
int &&ss = S().w.y;
687-
}
688-
689688
namespace ImplicitTemporaryCleanup {
690689
struct A { A(int); ~A(); };
691690
void g();

0 commit comments

Comments
 (0)