tbaeder updated this revision to Diff 456376.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D132727/new/
https://reviews.llvm.org/D132727
Files:
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
Index: clang/test/AST/Interp/arrays.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/Interp/arrays.cpp
@@ -0,0 +1,83 @@
+// 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, "");
+
+
+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, "");
+#pragma clang diagnostic pop
Index: clang/lib/AST/Interp/Program.cpp
===================================================================
--- clang/lib/AST/Interp/Program.cpp
+++ clang/lib/AST/Interp/Program.cpp
@@ -334,14 +334,15 @@
} 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);
}
}
Index: clang/lib/AST/Interp/Pointer.cpp
===================================================================
--- clang/lib/AST/Interp/Pointer.cpp
+++ clang/lib/AST/Interp/Pointer.cpp
@@ -106,7 +106,7 @@
// 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 @@
void Pointer::initialize() const {
assert(Pointee && "Cannot initialize null pointer");
Descriptor *Desc = getFieldDesc();
- if (Desc->isPrimitiveArray()) {
+
+ if (Desc->isArray() || Desc->isPrimitiveArray()) {
if (!Pointee->IsStatic) {
// Primitive array initializer.
InitMap *&Map = getInitMap();
Index: clang/lib/AST/Interp/Interp.h
===================================================================
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -721,6 +721,9 @@
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 @@
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>();
Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -171,8 +171,7 @@
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 @@
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());
+ auto Offset =
+ this->allocateLocalPrimitive(VD, *T, VD->getType().isConstQualified());
// Compile the initializer in its own scope.
{
ExprScope<Emitter> Scope(this);
@@ -254,14 +252,13 @@
return false;
}
// Set the value.
- return this->emitSetLocal(*T, Off, VD);
+ return this->emitSetLocal(*T, Offset, VD);
} else {
// Composite types - allocate storage and initialize it.
- if (auto Off = this->allocateLocal(VD)) {
- return this->visitLocalInitializer(VD->getInit(), *Off);
- } else {
+ if (auto Offset = this->allocateLocal(VD))
+ return this->visitLocalInitializer(VD->getInit(), *Offset);
+ else
return this->bail(VD);
- }
}
}
Index: clang/lib/AST/Interp/ByteCodeExprGen.h
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.h
+++ clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -40,17 +40,7 @@
class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
public Emitter {
protected:
- // Emitters for opcodes of various arities.
- using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &);
- using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &);
- using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType,
- const SourceInfo &);
-
- // Aliases for types defined in the emitter.
- using LabelTy = typename Emitter::LabelTy;
- using AddrTy = typename Emitter::AddrTy;
-
- // Reference to a function generating the pointer of an initialized object.s
+ // Reference to a function generating the pointer of an initialized object.
using InitFnRef = std::function<bool()>;
/// Current compilation context.
@@ -64,7 +54,7 @@
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 +67,9 @@
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 +123,45 @@
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.
@@ -230,9 +239,6 @@
/// Current scope.
VariableScope<Emitter> *VarScope = nullptr;
- /// Current argument index.
- llvm::Optional<uint64_t> ArrayIndex;
-
/// Flag indicating if return value is to be discarded.
bool DiscardResult = false;
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -231,12 +231,55 @@
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 @@
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>
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits