Skip to content

[clang][ExprConst] allow single element access of vector object to be constant expression #101126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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 GCC vector/ext_vector_type 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
110 changes: 106 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,27 @@ 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();
unsigned NumElements = VT->getNumElements();
if (Index == NumElements) {
if (Info.getLangOpts().CPlusPlus11)
Info.FFDiag(E, diag::note_constexpr_access_past_end)
<< handler.AccessKind;
else
Info.FFDiag(E);
return handler.failed();
}

if (Index > NumElements) {
Info.CCEDiag(E, diag::note_constexpr_array_index)
<< Index << /*array*/ 0 << NumElements;
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 +8562,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 +8904,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
2 changes: 1 addition & 1 deletion clang/test/AST/Interp/arrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ static_assert(*(&arr[0]) == 1, "");
static_assert(*(&arr[1]) == 2, "");

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

template<typename T>
constexpr T getElementOf(T* array, int i) {
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
49 changes: 24 additions & 25 deletions clang/test/AST/Interp/vectors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,39 @@

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}} \
// both-note {{cannot refer to element 12 of array of 4 elements in a constant expression}}


/// 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 +64,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 +77,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
Loading
Loading