Skip to content

Commit 471f87e

Browse files
committed
[clang][ExprConst] allow single element access of vector object to be constant expression
Supports both v[0] and v.x/v.r/v.s0 syntax. Selecting multiple elements is left as a future work.
1 parent 9e1ad3c commit 471f87e

File tree

5 files changed

+155
-26
lines changed

5 files changed

+155
-26
lines changed

clang/docs/ReleaseNotes.rst

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

145145
C++ Language Changes
146146
--------------------
147+
- Allow single element access of vector object to be constant expression.
148+
Supports the `V.xyzw` syntax and other tidbits as seen in OpenCL.
149+
Selecting multiple elements is left as a future work.
147150

148151
C++20 Feature Support
149152
^^^^^^^^^^^^^^^^^^^^^

clang/lib/AST/ExprConstant.cpp

+100-3
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ namespace {
221221
ArraySize = 2;
222222
MostDerivedLength = I + 1;
223223
IsArray = true;
224+
} else if (const auto *VT = Type->getAs<VectorType>()) {
225+
Type = VT->getElementType();
226+
ArraySize = VT->getNumElements();
227+
MostDerivedLength = I + 1;
228+
IsArray = true;
224229
} else if (const FieldDecl *FD = getAsField(Path[I])) {
225230
Type = FD->getType();
226231
ArraySize = 0;
@@ -437,6 +442,16 @@ namespace {
437442
MostDerivedArraySize = 2;
438443
MostDerivedPathLength = Entries.size();
439444
}
445+
void addVectorUnchecked(QualType EltTy, uint64_t Size, uint64_t Idx) {
446+
Entries.push_back(PathEntry::ArrayIndex(Idx));
447+
448+
// This is technically a most-derived object, though in practice this
449+
// is unlikely to matter.
450+
MostDerivedType = EltTy;
451+
MostDerivedIsArrayElement = true;
452+
MostDerivedArraySize = Size;
453+
MostDerivedPathLength = Entries.size();
454+
}
440455
void diagnoseUnsizedArrayPointerArithmetic(EvalInfo &Info, const Expr *E);
441456
void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E,
442457
const APSInt &N);
@@ -1732,6 +1747,11 @@ namespace {
17321747
if (checkSubobject(Info, E, Imag ? CSK_Imag : CSK_Real))
17331748
Designator.addComplexUnchecked(EltTy, Imag);
17341749
}
1750+
void addVectorElement(EvalInfo &Info, const Expr *E, QualType EltTy,
1751+
uint64_t Size, uint64_t Idx) {
1752+
if (checkSubobject(Info, E, CSK_VectorElement))
1753+
Designator.addVectorUnchecked(EltTy, Size, Idx);
1754+
}
17351755
void clearIsNullPointer() {
17361756
IsNullPtr = false;
17371757
}
@@ -3278,6 +3298,19 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E,
32783298
return true;
32793299
}
32803300

3301+
static bool HandleLValueVectorElement(EvalInfo &Info, const Expr *E,
3302+
LValue &LVal, QualType EltTy,
3303+
uint64_t Size, uint64_t Idx) {
3304+
if (Idx) {
3305+
CharUnits SizeOfElement;
3306+
if (!HandleSizeof(Info, E->getExprLoc(), EltTy, SizeOfElement))
3307+
return false;
3308+
LVal.Offset += SizeOfElement * Idx;
3309+
}
3310+
LVal.addVectorElement(Info, E, EltTy, Size, Idx);
3311+
return true;
3312+
}
3313+
32813314
/// Try to evaluate the initializer for a variable declaration.
32823315
///
32833316
/// \param Info Information about the ongoing evaluation.
@@ -3823,6 +3856,21 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
38233856
return handler.found(Index ? O->getComplexFloatImag()
38243857
: O->getComplexFloatReal(), ObjType);
38253858
}
3859+
} else if (const auto *VT = ObjType->getAs<VectorType>()) {
3860+
uint64_t Index = Sub.Entries[I].getAsArrayIndex();
3861+
if (Index >= VT->getNumElements()) {
3862+
if (Info.getLangOpts().CPlusPlus11)
3863+
Info.FFDiag(E, diag::note_constexpr_access_past_end)
3864+
<< handler.AccessKind;
3865+
else
3866+
Info.FFDiag(E);
3867+
return handler.failed();
3868+
}
3869+
3870+
ObjType = VT->getElementType();
3871+
3872+
assert(I == N - 1 && "extracting subobject of scalar?");
3873+
return handler.found(O->getVectorElt(Index), ObjType);
38263874
} else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
38273875
if (Field->isMutable() &&
38283876
!Obj.mayAccessMutableMembers(Info, handler.AccessKind)) {
@@ -8432,6 +8480,7 @@ class LValueExprEvaluator
84328480
bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
84338481
bool VisitCXXUuidofExpr(const CXXUuidofExpr *E);
84348482
bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
8483+
bool VisitExtVectorElementExpr(const ExtVectorElementExpr *E);
84358484
bool VisitUnaryDeref(const UnaryOperator *E);
84368485
bool VisitUnaryReal(const UnaryOperator *E);
84378486
bool VisitUnaryImag(const UnaryOperator *E);
@@ -8755,15 +8804,63 @@ bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {
87558804
return LValueExprEvaluatorBaseTy::VisitMemberExpr(E);
87568805
}
87578806

8807+
bool LValueExprEvaluator::VisitExtVectorElementExpr(
8808+
const ExtVectorElementExpr *E) {
8809+
bool Success = true;
8810+
8811+
APValue Val;
8812+
if (!Evaluate(Val, Info, E->getBase())) {
8813+
if (!Info.noteFailure())
8814+
return false;
8815+
Success = false;
8816+
}
8817+
8818+
SmallVector<uint32_t, 4> Indices;
8819+
E->getEncodedElementAccess(Indices);
8820+
// FIXME: support accessing more than one element
8821+
if (Indices.size() > 1)
8822+
return false;
8823+
8824+
if (Success) {
8825+
Result.setFrom(Info.Ctx, Val);
8826+
const auto *VT = E->getBase()->getType()->castAs<VectorType>();
8827+
HandleLValueVectorElement(Info, E, Result, VT->getElementType(),
8828+
VT->getNumElements(), Indices[0]);
8829+
}
8830+
8831+
return Success;
8832+
}
8833+
87588834
bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
8759-
// FIXME: Deal with vectors as array subscript bases.
8760-
if (E->getBase()->getType()->isVectorType() ||
8761-
E->getBase()->getType()->isSveVLSBuiltinType())
8835+
if (E->getBase()->getType()->isSveVLSBuiltinType())
87628836
return Error(E);
87638837

87648838
APSInt Index;
87658839
bool Success = true;
87668840

8841+
if (const auto *VT = E->getBase()->getType()->getAs<VectorType>()) {
8842+
APValue Val;
8843+
if (!Evaluate(Val, Info, E->getBase())) {
8844+
if (!Info.noteFailure())
8845+
return false;
8846+
Success = false;
8847+
}
8848+
8849+
if (!EvaluateInteger(E->getIdx(), Index, Info)) {
8850+
if (!Info.noteFailure())
8851+
return false;
8852+
Success = false;
8853+
}
8854+
8855+
if (Success) {
8856+
Result.setFrom(Info.Ctx, Val);
8857+
HandleLValueVectorElement(Info, E, Result, VT->getElementType(),
8858+
VT->getNumElements(), Index.getExtValue());
8859+
}
8860+
8861+
return Success;
8862+
}
8863+
87678864
// C++17's rules require us to evaluate the LHS first, regardless of which
87688865
// side is the base.
87698866
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/CodeGenCXX/temporaries.cpp

+21-22
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,27 @@ 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+
}
87+
6788
struct A {
6889
A();
6990
~A();
@@ -666,28 +687,6 @@ namespace Bitfield {
666687
int &&r = S().a;
667688
}
668689

669-
namespace Vector {
670-
typedef __attribute__((vector_size(16))) int vi4a;
671-
typedef __attribute__((ext_vector_type(4))) int vi4b;
672-
struct S {
673-
vi4a v;
674-
vi4b w;
675-
};
676-
// CHECK: alloca
677-
// CHECK: extractelement
678-
// CHECK: store i32 {{.*}}, ptr @_ZGRN6Vector1rE_
679-
// CHECK: store ptr @_ZGRN6Vector1rE_, ptr @_ZN6Vector1rE,
680-
int &&r = S().v[1];
681-
682-
// CHECK: alloca
683-
// CHECK: extractelement
684-
// CHECK: store i32 {{.*}}, ptr @_ZGRN6Vector1sE_
685-
// CHECK: store ptr @_ZGRN6Vector1sE_, ptr @_ZN6Vector1sE,
686-
int &&s = S().w[1];
687-
// FIXME PR16204: The following code leads to an assertion in Sema.
688-
//int &&s = S().w.y;
689-
}
690-
691690
namespace ImplicitTemporaryCleanup {
692691
struct A { A(int); ~A(); };
693692
void g();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 %s -Wno-uninitialized -std=c++17 -fsyntax-only -verify
2+
3+
namespace Vector {
4+
5+
using TwoIntsVecSize __attribute__((vector_size(8))) = int;
6+
7+
constexpr TwoIntsVecSize a = {1,2};
8+
static_assert(a[1] == 2);
9+
static_assert(a[2]); // expected-error {{not an integral constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
10+
11+
}
12+
13+
namespace ExtVector {
14+
15+
using FourIntsExtVec __attribute__((ext_vector_type(4))) = int;
16+
17+
constexpr FourIntsExtVec b = {1,2,3,4};
18+
static_assert(b[0] == 1 && b[1] == 2 && b[2] == 3 && b[3] == 4);
19+
static_assert(b.s0 == 1 && b.s1 == 2 && b.s2 == 3 && b.s3 == 4);
20+
static_assert(b.x == 1 && b.y == 2 && b.z == 3 && b.w == 4);
21+
static_assert(b.r == 1 && b.g == 2 && b.b == 3 && b.a == 4);
22+
static_assert(b[5]); // expected-error {{not an integral constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
23+
24+
// FIXME: support selecting multiple elements
25+
static_assert(b.lo.lo == 1); // expected-error {{not an integral constant expression}}
26+
// static_assert(b.lo.lo==1 && b.lo.hi==2 && b.hi.lo == 3 && b.hi.hi == 4);
27+
// static_assert(b.odd[0]==1 && b.odd[1]==2 && b.even[0] == 3 && b.even[1] == 4);
28+
29+
}

0 commit comments

Comments
 (0)