[clang] [clang][ExprConst] allow single element access of vector object to be constant expression (PR #72607)
Yuanfang Chen via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 22 23:18:57 PST 2023
https://github.com/yuanfang-chen updated https://github.com/llvm/llvm-project/pull/72607
>From b7d7c5fc70ffb792f67d007ec1bd71bcaed868fc Mon Sep 17 00:00:00 2001
From: Yuanfang Chen <tabloid.adroit at gmail.com>
Date: Fri, 17 Nov 2023 03:16:38 +0000
Subject: [PATCH] [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.
---
clang/lib/AST/ExprConstant.cpp | 104 +++++++++++++++++-
clang/lib/AST/Interp/State.h | 3 +-
clang/test/CodeGenCXX/temporaries.cpp | 43 ++++----
.../constexpr-vectors-access-elements.cpp | 29 +++++
4 files changed, 153 insertions(+), 26 deletions(-)
create mode 100644 clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3a41e9718bb5875..d699a3a8fcf3e68 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -221,6 +221,12 @@ namespace {
ArraySize = 2;
MostDerivedLength = I + 1;
IsArray = true;
+ } else if (Type->isVectorType()) {
+ const auto *VT = Type->castAs<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;
@@ -437,6 +443,16 @@ namespace {
MostDerivedArraySize = 2;
MostDerivedPathLength = Entries.size();
}
+ void addVectorUnchecked(QualType EltTy, uint64_t Size, uint64_t Idx) {
+ Entries.push_back(PathEntry::ArrayIndex(Idx));
+
+ // This is technically a most-derived object, though in practice this
+ // is unlikely to matter.
+ MostDerivedType = EltTy;
+ MostDerivedIsArrayElement = true;
+ MostDerivedArraySize = Size;
+ MostDerivedPathLength = Entries.size();
+ }
void diagnoseUnsizedArrayPointerArithmetic(EvalInfo &Info, const Expr *E);
void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E,
const APSInt &N);
@@ -1715,6 +1731,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.addVectorUnchecked(EltTy, Size, Idx);
+ }
void clearIsNullPointer() {
IsNullPtr = false;
}
@@ -3261,6 +3282,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.
@@ -3806,6 +3840,21 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
return handler.found(Index ? O->getComplexFloatImag()
: O->getComplexFloatReal(), ObjType);
}
+ } else if (ObjType->isVectorType()) {
+ uint64_t Index = Sub.Entries[I].getAsArrayIndex();
+ if (Index >= ObjType->castAs<VectorType>()->getNumElements()) {
+ if (Info.getLangOpts().CPlusPlus11)
+ Info.FFDiag(E, diag::note_constexpr_access_past_end)
+ << handler.AccessKind;
+ else
+ Info.FFDiag(E);
+ return handler.failed();
+ }
+
+ ObjType = ObjType->castAs<VectorType>()->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)) {
@@ -8408,6 +8457,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);
@@ -8721,15 +8771,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 VectorType *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 VectorType *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 f1e8e3618f34fe5..44d6c037c5ad955 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/CodeGenCXX/temporaries.cpp b/clang/test/CodeGenCXX/temporaries.cpp
index c5adb42a6f17374..135d2a356459272 100644
--- a/clang/test/CodeGenCXX/temporaries.cpp
+++ b/clang/test/CodeGenCXX/temporaries.cpp
@@ -64,6 +64,27 @@ 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();
@@ -666,28 +687,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];
- // FIXME PR16204: The following code leads to an assertion in Sema.
- //int &&s = 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 000000000000000..d31db4c496840e1
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
@@ -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);
+
+}
More information about the cfe-commits
mailing list