Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ sections with improvements to Clang's support for those languages.

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

C++17 Feature Support
^^^^^^^^^^^^^^^^^^^^^
Expand Down
102 changes: 98 additions & 4 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ namespace {
ArraySize = 2;
MostDerivedLength = I + 1;
IsArray = true;
} else if (const auto *VT = Type->getAs<VectorType>()) {
Type = VT->getElementType();
ArraySize = VT->getNumElements();
MostDerivedLength = I + 1;
IsArray = true;
} else if (const FieldDecl *FD = getAsField(Path[I])) {
Type = FD->getType();
ArraySize = 0;
Expand Down Expand Up @@ -268,7 +273,6 @@ namespace {
/// If the current array is an unsized array, the value of this is
/// undefined.
uint64_t MostDerivedArraySize;

/// The type of the most derived object referred to by this address.
QualType MostDerivedType;

Expand Down Expand Up @@ -442,6 +446,16 @@ namespace {
MostDerivedArraySize = 2;
MostDerivedPathLength = Entries.size();
}

void addVectorElementUnchecked(QualType EltTy, uint64_t Size,
uint64_t Idx) {
Entries.push_back(PathEntry::ArrayIndex(Idx));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sethp had a comment in the previous PR that is not address here: #72607 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I thought about having a new accessor of the sort "vectorIndex" but all it seems to achieve is just adding new API that returns does the exact same thing as array (other than perhaps adding a new meaning to PathEntry value). I will update it if you feel this makes sense.

MostDerivedType = EltTy;
MostDerivedPathLength = Entries.size();
MostDerivedArraySize = 0;
MostDerivedIsArrayElement = false;
}

void diagnoseUnsizedArrayPointerArithmetic(EvalInfo &Info, const Expr *E);
void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E,
const APSInt &N);
Expand Down Expand Up @@ -1737,6 +1751,11 @@ namespace {
if (checkSubobject(Info, E, Imag ? CSK_Imag : CSK_Real))
Designator.addComplexUnchecked(EltTy, Imag);
}
void addVectorElement(EvalInfo &Info, const Expr *E, QualType EltTy,
uint64_t Size, uint64_t Idx) {
if (checkSubobject(Info, E, CSK_VectorElement))
Designator.addVectorElementUnchecked(EltTy, Size, Idx);
}
void clearIsNullPointer() {
IsNullPtr = false;
}
Expand Down Expand Up @@ -3310,6 +3329,19 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E,
return true;
}

static bool HandleLValueVectorElement(EvalInfo &Info, const Expr *E,
LValue &LVal, QualType EltTy,
uint64_t Size, uint64_t Idx) {
if (Idx) {
CharUnits SizeOfElement;
if (!HandleSizeof(Info, E->getExprLoc(), EltTy, SizeOfElement))
return false;
LVal.Offset += SizeOfElement * Idx;
}
LVal.addVectorElement(Info, E, EltTy, Size, Idx);
return true;
}

/// Try to evaluate the initializer for a variable declaration.
///
/// \param Info Information about the ongoing evaluation.
Expand Down Expand Up @@ -3855,6 +3887,19 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
return handler.found(Index ? O->getComplexFloatImag()
: O->getComplexFloatReal(), ObjType);
}
} else if (const auto *VT = ObjType->getAs<VectorType>()) {
uint64_t Index = Sub.Entries[I].getAsArrayIndex();
if (Index >= VT->getNumElements()) {
if (Info.getLangOpts().CPlusPlus11)
Info.FFDiag(E, diag::note_constexpr_access_past_end)
<< handler.AccessKind;
else
Info.FFDiag(E);
return handler.failed();
}
ObjType = VT->getElementType();
assert(I == N - 1 && "extracting subobject of scalar?");
return handler.found(O->getVectorElt(Index), ObjType);
} else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
if (Field->isMutable() &&
!Obj.mayAccessMutableMembers(Info, handler.AccessKind)) {
Expand Down Expand Up @@ -8509,6 +8554,7 @@ class LValueExprEvaluator
bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
bool VisitCXXUuidofExpr(const CXXUuidofExpr *E);
bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
bool VisitExtVectorElementExpr(const ExtVectorElementExpr *E);
bool VisitUnaryDeref(const UnaryOperator *E);
bool VisitUnaryReal(const UnaryOperator *E);
bool VisitUnaryImag(const UnaryOperator *E);
Expand Down Expand Up @@ -8850,15 +8896,63 @@ bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {
return LValueExprEvaluatorBaseTy::VisitMemberExpr(E);
}

bool LValueExprEvaluator::VisitExtVectorElementExpr(
const ExtVectorElementExpr *E) {
bool Success = true;

APValue Val;
if (!Evaluate(Val, Info, E->getBase())) {
if (!Info.noteFailure())
return false;
Success = false;
}

SmallVector<uint32_t, 4> Indices;
E->getEncodedElementAccess(Indices);
// FIXME: support accessing more than one element
if (Indices.size() > 1)
return false;

if (Success) {
Result.setFrom(Info.Ctx, Val);
const auto *VT = E->getBase()->getType()->castAs<VectorType>();
HandleLValueVectorElement(Info, E, Result, VT->getElementType(),
VT->getNumElements(), Indices[0]);
}

return Success;
}

bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
// FIXME: Deal with vectors as array subscript bases.
if (E->getBase()->getType()->isVectorType() ||
E->getBase()->getType()->isSveVLSBuiltinType())
if (E->getBase()->getType()->isSveVLSBuiltinType())
return Error(E);

APSInt Index;
bool Success = true;

if (const auto *VT = E->getBase()->getType()->getAs<VectorType>()) {
APValue Val;
if (!Evaluate(Val, Info, E->getBase())) {
if (!Info.noteFailure())
return false;
Success = false;
}

if (!EvaluateInteger(E->getIdx(), Index, Info)) {
if (!Info.noteFailure())
return false;
Success = false;
}

if (Success) {
Result.setFrom(Info.Ctx, Val);
HandleLValueVectorElement(Info, E, Result, VT->getElementType(),
VT->getNumElements(), Index.getExtValue());
}

return Success;
}

// C++17's rules require us to evaluate the LHS first, regardless of which
// side is the base.
for (const Expr *SubExpr : {E->getLHS(), E->getRHS()}) {
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/Interp/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ enum CheckSubobjectKind {
CSK_ArrayToPointer,
CSK_ArrayIndex,
CSK_Real,
CSK_Imag
CSK_Imag,
CSK_VectorElement
};

namespace interp {
Expand Down
26 changes: 13 additions & 13 deletions clang/test/AST/Interp/builtin-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -866,11 +866,11 @@ namespace convertvector {
constexpr vector8BitInt128 from_vector8BitInt128_to_vector8BitInt128_var =
__builtin_convertvector((vector8BitInt128){0, 1, 2, 3, 4, 5, 6, 7},
vector8BitInt128);
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[0] == 0, ""); // ref-error {{not an integral constant expression}}
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[1] == 1, ""); // ref-error {{not an integral constant expression}}
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[2] == 2, ""); // ref-error {{not an integral constant expression}}
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[3] == 3, ""); // ref-error {{not an integral constant expression}}
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[4] == 4, ""); // ref-error {{not an integral constant expression}}
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[0] == 0, "");
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[1] == 1, "");
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[2] == 2, "");
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[3] == 3, "");
static_assert(from_vector8BitInt128_to_vector8BitInt128_var[4] == 4, "");
}

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

static_assert(vectorShuffle6[0] == 0, "");// ref-error {{not an integral constant expression}}
static_assert(vectorShuffle6[1] == 2, "");// ref-error {{not an integral constant expression}}
static_assert(vectorShuffle6[2] == 4, "");// ref-error {{not an integral constant expression}}
static_assert(vectorShuffle6[3] == 6, "");// ref-error {{not an integral constant expression}}
static_assert(vectorShuffle6[4] == 1, "");// ref-error {{not an integral constant expression}}
static_assert(vectorShuffle6[5] == 3, "");// ref-error {{not an integral constant expression}}
static_assert(vectorShuffle6[6] == 5, "");// ref-error {{not an integral constant expression}}
static_assert(vectorShuffle6[7] == 7, "");// ref-error {{not an integral constant expression}}
static_assert(vectorShuffle6[0] == 0, "");
static_assert(vectorShuffle6[1] == 2, "");
static_assert(vectorShuffle6[2] == 4, "");
static_assert(vectorShuffle6[3] == 6, "");
static_assert(vectorShuffle6[4] == 1, "");
static_assert(vectorShuffle6[5] == 3, "");
static_assert(vectorShuffle6[6] == 5, "");
static_assert(vectorShuffle6[7] == 7, "");

constexpr vector4char vectorShuffleFail1 = __builtin_shufflevector( // both-error {{must be initialized by a constant expression}}\
// 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}}
Expand Down
50 changes: 25 additions & 25 deletions clang/test/AST/Interp/vectors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,40 @@

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


/// FIXME: It would be nice if the note said 'vector' instead of 'array'.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolving this fixme comment would be a nice follow-up.

static_assert(A[12] == 4, ""); // ref-error {{not an integral constant expression}} \
// expected-error {{not an integral constant expression}} \
// expected-note {{cannot refer to element 12 of array of 4 elements in a constant expression}}
static_assert(A[12] == 4, ""); // both-error {{not an integral constant expression}} \
// expected-note {{cannot refer to element 12 of array of 4 elements in a constant expression}} \
// ref-note {{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why this note is being generated? I don't see how A[12] is a one-past-the-end pointer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just kept the original version of the PR, but the message "cannot refer to element 12 of array of 4 elements" seems correct here. I shall update this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, let me know if the updated changes are okay



/// VectorSplat casts
typedef __attribute__(( ext_vector_type(4) )) float float4;
constexpr float4 vec4_0 = (float4)0.5f;
static_assert(vec4_0[0] == 0.5, ""); // ref-error {{not an integral constant expression}}
static_assert(vec4_0[1] == 0.5, ""); // ref-error {{not an integral constant expression}}
static_assert(vec4_0[2] == 0.5, ""); // ref-error {{not an integral constant expression}}
static_assert(vec4_0[3] == 0.5, ""); // ref-error {{not an integral constant expression}}
static_assert(vec4_0[0] == 0.5, "");
static_assert(vec4_0[1] == 0.5, "");
static_assert(vec4_0[2] == 0.5, "");
static_assert(vec4_0[3] == 0.5, "");
constexpr int vec4_0_discarded = ((float4)12.0f, 0);


/// ImplicitValueInitExpr of vector type
constexpr float4 arr4[2] = {
{1,2,3,4},
};
static_assert(arr4[0][0] == 1, ""); // ref-error {{not an integral constant expression}}
static_assert(arr4[0][1] == 2, ""); // ref-error {{not an integral constant expression}}
static_assert(arr4[0][2] == 3, ""); // ref-error {{not an integral constant expression}}
static_assert(arr4[0][3] == 4, ""); // ref-error {{not an integral constant expression}}
static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
static_assert(arr4[0][0] == 1, "");
static_assert(arr4[0][1] == 2, "");
static_assert(arr4[0][2] == 3, "");
static_assert(arr4[0][3] == 4, "");
static_assert(arr4[1][0] == 0, "");
static_assert(arr4[1][0] == 0, "");
static_assert(arr4[1][0] == 0, "");
static_assert(arr4[1][0] == 0, "");


/// From constant-expression-cxx11.cpp
Expand Down Expand Up @@ -65,10 +65,10 @@ namespace {
namespace BoolToSignedIntegralCast{
typedef __attribute__((__ext_vector_type__(4))) unsigned int int4;
constexpr int4 intsT = (int4)true;
static_assert(intsT[0] == -1, "");// ref-error {{not an integral constant expression}}
static_assert(intsT[1] == -1, "");// ref-error {{not an integral constant expression}}
static_assert(intsT[2] == -1, "");// ref-error {{not an integral constant expression}}
static_assert(intsT[3] == -1, "");// ref-error {{not an integral constant expression}}
static_assert(intsT[0] == -1, "");
static_assert(intsT[1] == -1, "");
static_assert(intsT[2] == -1, "");
static_assert(intsT[3] == -1, "");
}

namespace VectorElementExpr {
Expand All @@ -78,8 +78,8 @@ namespace VectorElementExpr {
static_assert(oneElt == 3);

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

namespace Temporaries {
Expand Down
41 changes: 20 additions & 21 deletions clang/test/CodeGenCXX/temporaries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,26 @@ namespace RefTempSubobject {
constexpr const SelfReferential &sr = SelfReferential();
}

namespace Vector {
typedef __attribute__((vector_size(16))) int vi4a;
typedef __attribute__((ext_vector_type(4))) int vi4b;
struct S {
vi4a v;
vi4b w;
};

int &&r = S().v[1];
// CHECK: @_ZGRN6Vector1rE_ = internal global i32 0, align 4
// CHECK: @_ZN6Vector1rE = constant ptr @_ZGRN6Vector1rE_, align 8

int &&s = S().w[1];
// CHECK: @_ZGRN6Vector1sE_ = internal global i32 0, align 4
// CHECK: @_ZN6Vector1sE = constant ptr @_ZGRN6Vector1sE_, align 8

int &&t = S().w.y;
// CHECK: @_ZGRN6Vector1tE_ = internal global i32 0, align 4
// CHECK: @_ZN6Vector1tE = constant ptr @_ZGRN6Vector1tE_, align 8
}
struct A {
A();
~A();
Expand Down Expand Up @@ -665,27 +685,6 @@ namespace Bitfield {
int &&r = S().a;
}

namespace Vector {
typedef __attribute__((vector_size(16))) int vi4a;
typedef __attribute__((ext_vector_type(4))) int vi4b;
struct S {
vi4a v;
vi4b w;
};
// CHECK: alloca
// CHECK: extractelement
// CHECK: store i32 {{.*}}, ptr @_ZGRN6Vector1rE_
// CHECK: store ptr @_ZGRN6Vector1rE_, ptr @_ZN6Vector1rE,
int &&r = S().v[1];

// CHECK: alloca
// CHECK: extractelement
// CHECK: store i32 {{.*}}, ptr @_ZGRN6Vector1sE_
// CHECK: store ptr @_ZGRN6Vector1sE_, ptr @_ZN6Vector1sE,
int &&s = S().w[1];
int &&ss = S().w.y;
}

namespace ImplicitTemporaryCleanup {
struct A { A(int); ~A(); };
void g();
Expand Down
29 changes: 29 additions & 0 deletions clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %clang_cc1 %s -Wno-uninitialized -std=c++17 -fsyntax-only -verify

namespace Vector {

using TwoIntsVecSize __attribute__((vector_size(8))) = int;

constexpr TwoIntsVecSize a = {1,2};
static_assert(a[1] == 2);
static_assert(a[2]); // expected-error {{not an integral constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}

}

namespace ExtVector {

using FourIntsExtVec __attribute__((ext_vector_type(4))) = int;

constexpr FourIntsExtVec b = {1,2,3,4};
static_assert(b[0] == 1 && b[1] == 2 && b[2] == 3 && b[3] == 4);
static_assert(b.s0 == 1 && b.s1 == 2 && b.s2 == 3 && b.s3 == 4);
static_assert(b.x == 1 && b.y == 2 && b.z == 3 && b.w == 4);
static_assert(b.r == 1 && b.g == 2 && b.b == 3 && b.a == 4);
static_assert(b[5]); // expected-error {{not an integral constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}

// FIXME: support selecting multiple elements
static_assert(b.lo.lo == 1); // expected-error {{not an integral constant expression}}
// static_assert(b.lo.lo==1 && b.lo.hi==2 && b.hi.lo == 3 && b.hi.hi == 4);
// static_assert(b.odd[0]==1 && b.odd[1]==2 && b.even[0] == 3 && b.even[1] == 4);

}