Author: Vikram Hegde Date: 2024-08-07T16:11:08+05:30 New Revision: 77534291fcbd2c784c54e39a60895e4f60f19742
URL: https://github.com/llvm/llvm-project/commit/77534291fcbd2c784c54e39a60895e4f60f19742 DIFF: https://github.com/llvm/llvm-project/commit/77534291fcbd2c784c54e39a60895e4f60f19742.diff LOG: [clang][ExprConst] allow single element access of vector object to be constant expression (#101126) This is a slightly updated version of https://github.com/llvm/llvm-project/pull/72607, originally authored by @yuanfang-chen Added: clang/test/SemaCXX/constexpr-vectors-access-elements.cpp Modified: clang/docs/ReleaseNotes.rst clang/lib/AST/ExprConstant.cpp clang/lib/AST/Interp/State.h clang/test/AST/Interp/arrays.cpp clang/test/AST/Interp/builtin-functions.cpp clang/test/AST/Interp/vectors.cpp clang/test/CodeGenCXX/temporaries.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 829a759ca4a0be..978b4ac8ea2e37 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -75,6 +75,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 ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d4b9095c7509b3..4d2d05307a6de9 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -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; @@ -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; @@ -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)); + 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); @@ -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; } @@ -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. @@ -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)) { @@ -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); @@ -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()}) { diff --git a/clang/lib/AST/Interp/State.h b/clang/lib/AST/Interp/State.h index f1e8e3618f34fe..44d6c037c5ad95 100644 --- a/clang/lib/AST/Interp/State.h +++ b/clang/lib/AST/Interp/State.h @@ -44,7 +44,8 @@ enum CheckSubobjectKind { CSK_ArrayToPointer, CSK_ArrayIndex, CSK_Real, - CSK_Imag + CSK_Imag, + CSK_VectorElement }; namespace interp { diff --git a/clang/test/AST/Interp/arrays.cpp b/clang/test/AST/Interp/arrays.cpp index 612bd552aed480..98cd17276e0a06 100644 --- a/clang/test/AST/Interp/arrays.cpp +++ b/clang/test/AST/Interp/arrays.cpp @@ -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) { diff --git a/clang/test/AST/Interp/builtin-functions.cpp b/clang/test/AST/Interp/builtin-functions.cpp index 0a17106449faf3..b179298fee9bde 100644 --- a/clang/test/AST/Interp/builtin-functions.cpp +++ b/clang/test/AST/Interp/builtin-functions.cpp @@ -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 { @@ -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}} diff --git a/clang/test/AST/Interp/vectors.cpp b/clang/test/AST/Interp/vectors.cpp index 6991a348b07a94..b25d4d8a2df9aa 100644 --- a/clang/test/AST/Interp/vectors.cpp +++ b/clang/test/AST/Interp/vectors.cpp @@ -3,25 +3,24 @@ 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'. -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); @@ -29,14 +28,14 @@ constexpr int vec4_0_discarded = ((float4)12.0f, 0); 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 @@ -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 { @@ -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 { diff --git a/clang/test/CodeGenCXX/temporaries.cpp b/clang/test/CodeGenCXX/temporaries.cpp index 9f697bd9bf3efc..0990c804b8bcf8 100644 --- a/clang/test/CodeGenCXX/temporaries.cpp +++ b/clang/test/CodeGenCXX/temporaries.cpp @@ -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(); @@ -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(); diff --git a/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp b/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp new file mode 100644 index 00000000000000..08223e15feb721 --- /dev/null +++ b/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp @@ -0,0 +1,46 @@ +// 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}} + +constexpr struct { + TwoIntsVecSize b; +} Val = {{0,1}}; + +static_assert(Val.b[1] == 1); + +constexpr TwoIntsVecSize c[3] = {{0,1}, {2,3}, {4,5}}; +static_assert(c[0][0] == 0); +static_assert(c[1][1] == 3); +static_assert(c[2][3]); // expected-error {{not an integral constant expression}} expected-note {{cannot refer to element 3 of array of 2 elements}} + +// make sure clang rejects taking address of a vector element +static_assert(&a[0]); // expected-error {{address of vector element requested}} + +} + +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 {{cannot refer to element 5 of array of 4 elements}} + +// 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); + +// make sure clang rejects taking address of a vector element +static_assert(&b[1]); // expected-error {{address of vector element requested}} + +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits