[clang] 3a7d476 - [clang][Interp] Implement array initializers and subscript expressions
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 7 22:31:56 PDT 2022
Author: Timm Bäder
Date: 2022-09-08T07:31:07+02:00
New Revision: 3a7d476087df175b6fe056e7c20ac9707019e92b
URL: https://github.com/llvm/llvm-project/commit/3a7d476087df175b6fe056e7c20ac9707019e92b
DIFF: https://github.com/llvm/llvm-project/commit/3a7d476087df175b6fe056e7c20ac9707019e92b.diff
LOG: [clang][Interp] Implement array initializers and subscript expressions
Differential Revision: https://reviews.llvm.org/D132727
Added:
Modified:
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/ByteCodeExprGen.h
clang/lib/AST/Interp/ByteCodeStmtGen.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/Pointer.cpp
clang/lib/AST/Interp/Program.cpp
clang/test/AST/Interp/arrays.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index cf4cbde37975..790f58f67e68 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -231,12 +231,55 @@ bool ByteCodeExprGen<Emitter>::VisitImplicitValueInitExpr(const ImplicitValueIni
return false;
}
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitArraySubscriptExpr(
+ const ArraySubscriptExpr *E) {
+ const Expr *Base = E->getBase();
+ const Expr *Index = E->getIdx();
+
+ // Take pointer of LHS, add offset from RHS, narrow result.
+ // What's left on the stack after this is a pointer.
+ if (Optional<PrimType> IndexT = classify(Index->getType())) {
+ if (!this->Visit(Base))
+ return false;
+
+ if (!this->Visit(Index))
+ return false;
+
+ if (!this->emitAddOffset(*IndexT, E))
+ return false;
+
+ if (!this->emitNarrowPtr(E))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitInitListExpr(const InitListExpr *E) {
+ for (const Expr *Init : E->inits()) {
+ if (!this->visit(Init))
+ return false;
+ }
+ return true;
+}
+
template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitSubstNonTypeTemplateParmExpr(
const SubstNonTypeTemplateParmExpr *E) {
return this->visit(E->getReplacement());
}
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitConstantExpr(const ConstantExpr *E) {
+ // TODO: Check if the ConstantExpr already has a value set and if so,
+ // use that instead of evaluating it again.
+ return this->visit(E->getSubExpr());
+}
+
template <class Emitter>
bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true);
@@ -492,11 +535,62 @@ ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) {
return Local.Offset;
}
+// NB: When calling this function, we have a pointer to the
+// array-to-initialize on the stack.
template <class Emitter>
-bool ByteCodeExprGen<Emitter>::visitInitializer(
- const Expr *Init, InitFnRef InitFn) {
- OptionScope<Emitter> Scope(this, InitFn);
- return this->Visit(Init);
+bool ByteCodeExprGen<Emitter>::visitArrayInitializer(const Expr *Initializer) {
+ assert(Initializer->getType()->isArrayType());
+
+ // TODO: Fillers?
+ if (const auto *InitList = dyn_cast<InitListExpr>(Initializer)) {
+ unsigned ElementIndex = 0;
+ for (const Expr *Init : InitList->inits()) {
+ QualType InitType = Init->getType();
+
+ if (InitType->isArrayType()) {
+ // Advance the pointer currently on the stack to the given
+ // dimension and narrow().
+ if (!this->emitDupPtr(Init))
+ return false;
+ if (!this->emitConstUint32(ElementIndex, Init))
+ return false;
+ if (!this->emitAddOffsetUint32(Init))
+ return false;
+ if (!this->emitNarrowPtr(Init))
+ return false;
+ if (!visitArrayInitializer(Init))
+ return false;
+ if (!this->emitPopPtr(Init))
+ return false;
+ } else if (Optional<PrimType> T = classify(InitType)) {
+ // Visit the primitive element like normal.
+ if (!this->visit(Init))
+ return false;
+ if (!this->emitInitElem(*T, ElementIndex, Init))
+ return false;
+ } else {
+ assert(false && "Unhandled type in array initializer initlist");
+ }
+
+ ++ElementIndex;
+ }
+
+ } else {
+ assert(false && "Unknown expression for array initialization");
+ }
+
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitInitializer(const Expr *Initializer) {
+ QualType InitializerType = Initializer->getType();
+
+ if (InitializerType->isArrayType())
+ return visitArrayInitializer(Initializer);
+
+ // Otherwise, visit the expression like normal.
+ return this->Visit(Initializer);
}
template <class Emitter>
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index 6132e2b67435..6a74bc371855 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -64,7 +64,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args)
: Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {}
- // Expression visitors - result returned on stack.
+ // Expression visitors - result returned on interp stack.
bool VisitCastExpr(const CastExpr *E);
bool VisitIntegerLiteral(const IntegerLiteral *E);
bool VisitParenExpr(const ParenExpr *E);
@@ -77,6 +77,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool VisitDeclRefExpr(const DeclRefExpr *E);
bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E);
bool VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E);
+ bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
+ bool VisitInitListExpr(const InitListExpr *E);
+ bool VisitConstantExpr(const ConstantExpr *E);
protected:
bool visitExpr(const Expr *E) override;
@@ -130,29 +133,45 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool discard(const Expr *E);
/// Evaluates an expression and places result on stack.
bool visit(const Expr *E);
- /// Compiles an initializer for a local.
- bool visitInitializer(const Expr *E, InitFnRef GenPtr);
+ /// Compiles an initializer.
+ bool visitInitializer(const Expr *E);
+ /// Compiles an array initializer.
+ bool visitArrayInitializer(const Expr *Initializer);
/// Visits an expression and converts it to a boolean.
bool visitBool(const Expr *E);
/// Visits an initializer for a local.
bool visitLocalInitializer(const Expr *Init, unsigned I) {
- return visitInitializer(Init, [this, I, Init] {
- return this->emitGetPtrLocal(I, Init);
- });
+ if (!this->emitGetPtrLocal(I, Init))
+ return false;
+
+ if (!visitInitializer(Init))
+ return false;
+
+ return this->emitPopPtr(Init);
}
/// Visits an initializer for a global.
bool visitGlobalInitializer(const Expr *Init, unsigned I) {
- return visitInitializer(Init, [this, I, Init] {
- return this->emitGetPtrGlobal(I, Init);
- });
+ if (!this->emitGetPtrGlobal(I, Init))
+ return false;
+
+ if (!visitInitializer(Init))
+ return false;
+
+ return this->emitPopPtr(Init);
}
/// Visits a delegated initializer.
bool visitThisInitializer(const Expr *I) {
- return visitInitializer(I, [this, I] { return this->emitThis(I); });
+ if (!this->emitThis(I))
+ return false;
+
+ if (!visitInitializer(I))
+ return false;
+
+ return this->emitPopPtr(I);
}
/// Creates a local primitive value.
diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index f279267bbe1f..c4a1efc62935 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -171,8 +171,7 @@ bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) {
return this->emitRet(*ReturnType, RS);
} else {
// RVO - construct the value in the return location.
- auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); };
- if (!this->visitInitializer(RE, ReturnLocation))
+ if (!this->visitInitializer(RE))
return false;
this->emitCleanup();
return this->emitRetVoid(RS);
@@ -232,21 +231,20 @@ bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) {
template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) {
- auto DT = VD->getType();
-
if (!VD->hasLocalStorage()) {
// No code generation required.
return true;
}
// Integers, pointers, primitives.
- if (Optional<PrimType> T = this->classify(DT)) {
+ if (Optional<PrimType> T = this->classify(VD->getType())) {
const Expr *Init = VD->getInit();
if (!Init)
return false;
- auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified());
+ unsigned Offset =
+ this->allocateLocalPrimitive(VD, *T, VD->getType().isConstQualified());
// Compile the initializer in its own scope.
{
ExprScope<Emitter> Scope(this);
@@ -254,15 +252,14 @@ bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) {
return false;
}
// Set the value.
- return this->emitSetLocal(*T, Off, VD);
- } else {
- // Composite types - allocate storage and initialize it.
- if (auto Off = this->allocateLocal(VD)) {
- return this->visitLocalInitializer(VD->getInit(), *Off);
- } else {
- return this->bail(VD);
- }
+ return this->emitSetLocal(*T, Offset, VD);
}
+
+ // Composite types - allocate storage and initialize it.
+ if (Optional<unsigned> Offset = this->allocateLocal(VD))
+ return this->visitLocalInitializer(VD->getInit(), *Offset);
+
+ return this->bail(VD);
}
namespace clang {
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index ceb0db5c3c26..0a5c8dc847c3 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -721,6 +721,9 @@ bool InitPop(InterpState &S, CodePtr OpPC) {
return true;
}
+/// 1) Pops the value from the stack
+/// 2) Peeks a pointer and gets its index \Idx
+/// 3) Sets the value on the pointer, leaving the pointer on the stack.
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
const T &Value = S.Stk.pop<T>();
@@ -732,6 +735,7 @@ bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
return true;
}
+/// The same as InitElem, but pops the pointer as well.
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
const T &Value = S.Stk.pop<T>();
diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp
index ef2638e2a36b..cb687377e97b 100644
--- a/clang/lib/AST/Interp/Pointer.cpp
+++ b/clang/lib/AST/Interp/Pointer.cpp
@@ -106,7 +106,7 @@ APValue Pointer::toAPValue() const {
// Build the path into the object.
Pointer Ptr = *this;
- while (Ptr.isField()) {
+ while (Ptr.isField() || Ptr.isArrayElement()) {
if (Ptr.isArrayElement()) {
Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
Ptr = Ptr.getArray();
@@ -154,7 +154,8 @@ bool Pointer::isInitialized() const {
void Pointer::initialize() const {
assert(Pointee && "Cannot initialize null pointer");
Descriptor *Desc = getFieldDesc();
- if (Desc->isPrimitiveArray()) {
+
+ if (Desc->isArray()) {
if (!Pointee->IsStatic) {
// Primitive array initializer.
InitMap *&Map = getInitMap();
diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp
index e310c9678140..0d4d84eab376 100644
--- a/clang/lib/AST/Interp/Program.cpp
+++ b/clang/lib/AST/Interp/Program.cpp
@@ -334,14 +334,15 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
} else {
// Arrays of composites. In this case, the array is a list of pointers,
// followed by the actual elements.
- Descriptor *Desc =
+ Descriptor *ElemDesc =
createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary);
- if (!Desc)
+ if (!ElemDesc)
return nullptr;
- InterpSize ElemSize = Desc->getAllocSize() + sizeof(InlineDescriptor);
+ InterpSize ElemSize =
+ ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems)
return {};
- return allocateDescriptor(D, Desc, NumElems, IsConst, IsTemporary,
+ return allocateDescriptor(D, ElemDesc, NumElems, IsConst, IsTemporary,
IsMutable);
}
}
diff --git a/clang/test/AST/Interp/arrays.cpp b/clang/test/AST/Interp/arrays.cpp
index 74fd1f0fbb3f..88bb0a8eadb5 100644
--- a/clang/test/AST/Interp/arrays.cpp
+++ b/clang/test/AST/Interp/arrays.cpp
@@ -1,13 +1,85 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s
// RUN: %clang_cc1 -verify=ref %s
+constexpr int m = 3;
+constexpr const int *foo[][5] = {
+ {nullptr, &m, nullptr, nullptr, nullptr},
+ {nullptr, nullptr, &m, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, &m, nullptr},
+};
+
+static_assert(foo[0][0] == nullptr, "");
+static_assert(foo[0][1] == &m, "");
+static_assert(foo[0][2] == nullptr, "");
+static_assert(foo[0][3] == nullptr, "");
+static_assert(foo[0][4] == nullptr, "");
+static_assert(foo[1][0] == nullptr, "");
+static_assert(foo[1][1] == nullptr, "");
+static_assert(foo[1][2] == &m, "");
+static_assert(foo[1][3] == nullptr, "");
+static_assert(foo[1][4] == nullptr, "");
+static_assert(foo[2][0] == nullptr, "");
+static_assert(foo[2][1] == nullptr, "");
+static_assert(foo[2][2] == nullptr, "");
+static_assert(foo[2][3] == &m, "");
+static_assert(foo[2][4] == nullptr, "");
+
+
+/// A init list for a primitive value.
+constexpr int f{5};
+static_assert(f == 5, "");
+
+
+constexpr int getElement(int i) {
+ int values[] = {1, 4, 9, 16, 25, 36};
+ return values[i];
+}
+static_assert(getElement(1) == 4, "");
+static_assert(getElement(5) == 36, "");
+
+
+template<typename T>
+constexpr T getElementOf(T* array, int i) {
+ return array[i];
+}
+static_assert(getElementOf(foo[0], 1) == &m, "");
+
-/// expected-no-diagnostics
-/// ref-no-diagnostics
+constexpr int data[] = {5, 4, 3, 2, 1};
+static_assert(data[0] == 4, ""); // expected-error{{failed}} \
+ // expected-note{{5 == 4}} \
+ // ref-error{{failed}} \
+ // ref-note{{5 == 4}}
+
+
+constexpr int dynamic[] = {
+ f, 3, 2 + 5, data[3], *getElementOf(foo[2], 3)
+};
+static_assert(dynamic[0] == f, "");
+static_assert(dynamic[3] == 2, "");
+
+
+constexpr int dependent[4] = {
+ 0, 1, dependent[0], dependent[1]
+};
+static_assert(dependent[2] == dependent[0], "");
+static_assert(dependent[3] == dependent[1], "");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc99-extensions"
#pragma clang diagnostic ignored "-Winitializer-overrides"
+constexpr int DI[] = {
+ [0] = 10,
+ [1] = 20,
+ 30,
+ 40,
+ [1] = 50
+};
+static_assert(DI[0] == 10, "");
+static_assert(DI[1] == 50, "");
+static_assert(DI[2] == 30, "");
+static_assert(DI[3] == 40, "");
+
/// FIXME: The example below tests ImplicitValueInitExprs, but we can't
/// currently evaluate other parts of it.
#if 0
More information about the cfe-commits
mailing list