-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[clang][ExprConst] allow single element access of vector object to be constant expression #72607
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
Conversation
@llvm/pr-subscribers-clang Author: Yuanfang Chen (yuanfang-chen) ChangesSupports both v[0] and v.x/v.r/v.s0 syntax. Selecting multiple elements is left as a future work. Full diff: https://github.com/llvm/llvm-project/pull/72607.diff 4 Files Affected:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index eea0827d6f7a8a1..7468dc5c71fa895 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -436,6 +436,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);
@@ -1714,6 +1724,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;
}
@@ -3294,6 +3309,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.
@@ -3839,6 +3867,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)) {
@@ -8294,6 +8337,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);
@@ -8607,15 +8651,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);
+
+}
|
You can test this locally with the following command:git-clang-format --diff 9e1ad3cff6a855fdfdc1d91323e2021726da04ea 471f87e727d71e3984d533eeb9db9ebab40e63ff -- clang/test/SemaCXX/constexpr-vectors-access-elements.cpp clang/lib/AST/ExprConstant.cpp clang/lib/AST/Interp/State.h clang/test/CodeGenCXX/temporaries.cpp View the diff from clang-format here.diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 9e2fc9b1af..3ebe77ae1f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -3861,7 +3861,7 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
if (Index >= VT->getNumElements()) {
if (Info.getLangOpts().CPlusPlus11)
Info.FFDiag(E, diag::note_constexpr_access_past_end)
- << handler.AccessKind;
+ << handler.AccessKind;
else
Info.FFDiag(E);
return handler.failed();
|
ping? |
26a20b1
to
b7d7c5f
Compare
ping .. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also add a release note about the changes.
… constant expression Supports both v[0] and v.x/v.r/v.s0 syntax. Selecting multiple elements is left as a future work.
b7d7c5f
to
471f87e
Compare
ping? |
This basically looks good to me, but I don't know much about vector types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking into this! I was surprised to find that clang
didn't support element accesses in a constexpr
context, I'm glad you've put the effort in to get it working!
Two minor notes below, otherwise LGTM.
MostDerivedIsArrayElement = true; | ||
MostDerivedArraySize = Size; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this: it seems to indicate that an expression like &V[0]
ought to be valid, but it's often not possible to take a pointer to a vector element (i.e. when they're living in a vector register).
That said, I see it's what the Complex
type is doing, and I whose elements I think are also not individually addressable, so 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, I see it's what the
Complex
type is doing, and I whose elements I think are also not individually addressable, so 🤷
C23 6.2.5p17: Each complex type has the same representation and alignment requirements as an array type containing exactly two elements of the corresponding real type; the first element is equal to the real part, and the second element to the imaginary part, of the complex number.
CC @jcranmer-intel @arsenm for some other opinions on whether you should be able to address a vector type in a constant expression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think accessing individual fields should be OK. Taking the address of a vector element feels wrong, but I haven't thought too deeply about it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Until we have evidence that &V[0]
makes sense for vector types, I think we should disallow it. It's easier for us to introduce support once we have a use case than to have to maintain support should someone start relying on this without us intending it as a stable extension.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mostly makes sense to me, @AaronBallman does this look good to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, this fell off my radar for a while. The only concern I have is with accidentally allowing &V[0]
to mean something; it would be good to reject that and add a test for that situation. Otherwise, this looks reasonable to me.
MostDerivedIsArrayElement = true; | ||
MostDerivedArraySize = Size; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Until we have evidence that &V[0]
makes sense for vector types, I think we should disallow it. It's easier for us to introduce support once we have a use case than to have to maintain support should someone start relying on this without us intending it as a stable extension.
@yuanfang-chen , any plans to continue with this PR ? |
@yuanfang-chen , @AaronBallman, @shafik, are we still actively looking into this ? (I would be willing to commandeer this if its not high on your priority list) |
Hello @vikramRH, please feel free to commandeer this. |
Thanks @yuanfang-chen. Also, clang already rejects expressions like &V[0] (https://godbolt.org/z/eGcxzGo66), which is also true with constexprs and this PR. What's the specific concern here ? |
Ah, sorry: my specific question is whether the APValue::LValuePathEntry ought to grow an understanding of vector types rather than re-using the array machinery, as here. It sounds like arrays express a couple of properties vectors don't, so there's potential for the evaluator to want to distinguish an LValuePath that ends in an array element from one that references a vector component. That said, I don't have a strong sense of whether it's worth doing. "Sema won't build an AST that the evaluator would need to reject" is a fair way to avoid telling the two apart, but seems much harder to me to demonstrate: "none of the ways we tried to get the evaluator to mistreat a vector as an array" is only the same property if we've managed to test exhaustively. For what my non-expert opinion is worth, something like this seems more robust to me: void addVectorUnchecked(QualType EltTy, uint64_t Size, uint64_t Idx) {
- Entries.push_back(PathEntry::ArrayIndex(Idx));
+ Entries.push_back(PathEntry::VectorElement(Idx));
- // This is technically a most-derived object, though in practice this
- // is unlikely to matter.
MostDerivedType = EltTy;
- MostDerivedIsArrayElement = true;
- MostDerivedArraySize = Size; (assuming that's coherent, it's been a few months) |
… constant expression (#101126) This is a slightly updated version of #72607, originally authored by @yuanfang-chen
closing this, since its handled via #101126 |
… constant expression (llvm#101126) This is a slightly updated version of llvm#72607, originally authored by @yuanfang-chen Change-Id: Ifdf143d4d766b076bf3010048ab121932c83bb1e
… constant expression (llvm#101126) This is a slightly updated version of llvm#72607, originally authored by @yuanfang-chen Change-Id: Id9f94ebb5ce74e853ad9704c97170a128bfd2bc9
Supports both v[0] and v.x/v.r/v.s0 syntax.
Selecting multiple elements is left as a future work.