tbaeder created this revision.
tbaeder added reviewers: erichkeane, aaron.ballman, shafik, tahonermann.
Herald added a project: All.
tbaeder requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Uploading this now to get some early feedback on the approach and maybe find 
some better tests.

Pointers in the VM are implemented through the `Pointer` class. Each `Pointer` 
points to a `Block`, which represent memory. However, each `Block` also knows 
all the `Pointer`s pointing at it. I've used this to find simply replace a 
`Block` representing an array with a larger one and update all pointers 
pointing to said block (`Block::transferPointers()`). This is the core of it 
all, but it requires quite a few smaller changes that could be broken out of 
this patch of course. Like enabling pointer tracking for static `Block`s.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D133753

Files:
  clang/lib/AST/Interp/Boolean.h
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/Context.cpp
  clang/lib/AST/Interp/Descriptor.cpp
  clang/lib/AST/Interp/Descriptor.h
  clang/lib/AST/Interp/EvalEmitter.cpp
  clang/lib/AST/Interp/Integral.h
  clang/lib/AST/Interp/Interp.h
  clang/lib/AST/Interp/InterpBlock.cpp
  clang/lib/AST/Interp/InterpBlock.h
  clang/lib/AST/Interp/InterpFrame.cpp
  clang/lib/AST/Interp/InterpFrame.h
  clang/lib/AST/Interp/InterpStack.h
  clang/lib/AST/Interp/Pointer.cpp
  clang/lib/AST/Interp/Pointer.h
  clang/lib/AST/Interp/Program.cpp
  clang/lib/AST/Interp/Program.h
  clang/test/AST/Interp/arrays.cpp

Index: clang/test/AST/Interp/arrays.cpp
===================================================================
--- clang/test/AST/Interp/arrays.cpp
+++ clang/test/AST/Interp/arrays.cpp
@@ -98,3 +98,59 @@
 struct fred y [] = { [0] = { .s[0] = 'q' } };
 #endif
 #pragma clang diagnostic pop
+
+namespace indices {
+  constexpr int first[] = {1};
+  constexpr int firstValue = first[2]; // ref-error {{must be initialized by a constant expression}} \
+                                       // ref-note {{cannot refer to element 2 of array of 1}} \
+                                       // expected-error {{must be initialized by a constant expression}} \
+                                       // expected-note {{cannot refer to element 2 of array of 1}}
+
+  constexpr int second[10] = {17};
+  constexpr int secondValue = second[10];// ref-error {{must be initialized by a constant expression}} \
+                                         // ref-note {{read of dereferenced one-past-the-end pointer}} \
+                                         // expected-error {{must be initialized by a constant expression}} \
+                                         // expected-note {{cannot refer to element 10 of array of 10}}
+
+  constexpr int negative = second[-2]; // ref-error {{must be initialized by a constant expression}} \
+                                       // ref-note {{cannot refer to element -2 of array of 10}} \
+                                       // expected-error {{must be initialized by a constant expression}} \
+                                       // expected-note {{cannot refer to element -2 of array of 10}}
+};
+
+namespace fillers {
+  constexpr int k[3] = {1337};
+  constexpr int foo(int m) {
+    return k[m];
+  }
+  static_assert(foo(0) == 1337, "");
+  static_assert(foo(1) == 0, "");
+  static_assert(foo(2) == 0, "");
+
+  constexpr int N = 12;
+  constexpr int local(unsigned i) {
+    int arr[N] = {1,2,3};
+    return arr[i];
+  }
+  static_assert(local(0) == 1, "");
+  static_assert(local(1) == 2, "");
+  static_assert(local(2) == 3, "");
+  static_assert(local(3) == 0, "");
+  static_assert(local(N - 1) == 0, "");
+
+  /// FIXME: Non-primitive array fillers are not handled properly currently,
+  ///   but nobody notices right now because all fillers are 0 anyway and
+  ///   that's also was we memset() the memory to.
+  constexpr int twodim[2][4] = {
+    {1,2},
+    {3,4}
+  };
+  static_assert(twodim[0][0] == 1, "");
+  static_assert(twodim[0][1] == 2, "");
+  static_assert(twodim[0][2] == 0, "");
+  static_assert(twodim[0][3] == 0, "");
+  static_assert(twodim[1][0] == 3, "");
+  static_assert(twodim[1][1] == 4, "");
+  static_assert(twodim[1][2] == 0, "");
+  static_assert(twodim[1][3] == 0, "");
+};
Index: clang/lib/AST/Interp/Program.h
===================================================================
--- clang/lib/AST/Interp/Program.h
+++ clang/lib/AST/Interp/Program.h
@@ -56,7 +56,12 @@
   /// Returns the value of a global.
   Block *getGlobal(unsigned Idx) {
     assert(Idx < Globals.size());
-    return Globals[Idx]->block();
+    return Globals[Idx].block();
+  }
+
+  /// Checks whether the given pointer points to a global variable.
+  bool isGlobalPointer(const Pointer &P) const {
+    return false;
   }
 
   /// Finds a global's index.
@@ -69,7 +74,7 @@
   llvm::Optional<unsigned> getOrCreateDummy(const ParmVarDecl *PD);
 
   /// Creates a global and returns its index.
-  llvm::Optional<unsigned> createGlobal(const ValueDecl *VD);
+  llvm::Optional<unsigned> createGlobal(const ValueDecl *VD, const Expr *E);
 
   /// Creates a global from a lifetime-extended temporary.
   llvm::Optional<unsigned> createGlobal(const Expr *E);
@@ -111,7 +116,8 @@
   /// Creates a descriptor for a composite type.
   Descriptor *createDescriptor(const DeclTy &D, const Type *Ty,
                                bool IsConst = false, bool IsTemporary = false,
-                               bool IsMutable = false);
+                               bool IsMutable = false,
+                               const Expr *Init = nullptr);
 
   /// Context to manage declaration lifetimes.
   class DeclScope {
@@ -134,7 +140,8 @@
   friend class DeclScope;
 
   llvm::Optional<unsigned> createGlobal(const DeclTy &D, QualType Ty,
-                                        bool IsStatic, bool IsExtern);
+                                        bool IsStatic, bool IsExtern,
+                                        const Expr *Init = nullptr);
 
   /// Reference to the VM context.
   Context &Ctx;
@@ -154,35 +161,12 @@
   /// Custom allocator for global storage.
   using PoolAllocTy = llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator>;
 
-  /// Descriptor + storage for a global object.
-  ///
-  /// Global objects never go out of scope, thus they do not track pointers.
-  class Global {
-  public:
-    /// Create a global descriptor for string literals.
-    template <typename... Tys>
-    Global(Tys... Args) : B(std::forward<Tys>(Args)...) {}
-
-    /// Allocates the global in the pool, reserving storate for data.
-    void *operator new(size_t Meta, PoolAllocTy &Alloc, size_t Data) {
-      return Alloc.Allocate(Meta + Data, alignof(void *));
-    }
-
-    /// Return a pointer to the data.
-    char *data() { return B.data(); }
-    /// Return a pointer to the block.
-    Block *block() { return &B; }
-
-  private:
-    /// Required metadata - does not actually track pointers.
-    Block B;
-  };
-
   /// Allocator for globals.
   PoolAllocTy Allocator;
 
-  /// Global objects.
-  std::vector<Global *> Globals;
+  /// All global variables we've created. We use Pointers instances here
+  /// so we get pointer tracking in the blocks.
+  std::vector<Pointer> Globals;
   /// Cached global indices.
   llvm::DenseMap<const void *, unsigned> GlobalIndices;
 
@@ -198,6 +182,15 @@
     return new (Allocator) Descriptor(std::forward<Ts>(Args)...);
   }
 
+  /// Creates a new Block.
+  template <typename... Ts>
+  Block *allocateBlock(const Descriptor *D, Ts &&...Args) {
+    Block *B = static_cast<Block *>(
+        Allocator.Allocate(sizeof(Block) + D->getAllocSize(), alignof(void *)));
+    new (B) Block(std::forward<Ts>(Args)...);
+    return B;
+  }
+
   /// No declaration ID.
   static constexpr unsigned NoDeclaration = (unsigned)-1;
   /// Last declaration ID.
Index: clang/lib/AST/Interp/Program.cpp
===================================================================
--- clang/lib/AST/Interp/Program.cpp
+++ clang/lib/AST/Interp/Program.cpp
@@ -53,21 +53,21 @@
   }
 
   // Create a descriptor for the string.
-  Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1,
-                                        /*isConst=*/true,
-                                        /*isTemporary=*/false,
-                                        /*isMutable=*/false);
+  Descriptor *Desc =
+      allocateDescriptor(S, CharType, S->getLength() + 1,
+                         /*ArrayFiller=*/nullptr, S->getLength() + 1,
+                         /*IsConst=*/true,
+                         /*IsTemporary=*/false,
+                         /*IsMutable=*/false);
 
   // Allocate storage for the string.
   // The byte length does not include the null terminator.
   unsigned I = Globals.size();
-  unsigned Sz = Desc->getAllocSize();
-  auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true,
-                                       /*isExtern=*/false);
-  Globals.push_back(G);
+  auto *G = allocateBlock(Desc, Desc, true, false);
+  Globals.emplace_back(G);
 
   // Construct the string in storage.
-  const Pointer Ptr(G->block());
+  const Pointer Ptr(G);
   for (unsigned I = 0, N = S->getLength(); I <= N; ++I) {
     Pointer Field = Ptr.atIndex(I).narrow();
     const uint32_t CodePoint = I == N ? 0 : S->getCodeUnit(I);
@@ -96,7 +96,7 @@
 
 Pointer Program::getPtrGlobal(unsigned Idx) {
   assert(Idx < Globals.size());
-  return Pointer(Globals[Idx]->block());
+  return Globals[Idx];
 }
 
 llvm::Optional<unsigned> Program::getGlobal(const ValueDecl *VD) {
@@ -127,7 +127,7 @@
   if (auto Idx = getGlobal(VD))
     return Idx;
 
-  if (auto Idx = createGlobal(VD)) {
+  if (auto Idx = createGlobal(VD, nullptr)) {
     GlobalIndices[VD] = *Idx;
     return Idx;
   }
@@ -153,7 +153,8 @@
   return {};
 }
 
-llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) {
+llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD,
+                                               const Expr *Init) {
   bool IsStatic, IsExtern;
   if (auto *Var = dyn_cast<VarDecl>(VD)) {
     IsStatic = !Var->hasLocalStorage();
@@ -162,7 +163,7 @@
     IsStatic = false;
     IsExtern = true;
   }
-  if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern)) {
+  if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, Init)) {
     for (const Decl *P = VD; P; P = P->getPreviousDecl())
       GlobalIndices[P] = *Idx;
     return *Idx;
@@ -175,7 +176,8 @@
 }
 
 llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
-                                               bool IsStatic, bool IsExtern) {
+                                               bool IsStatic, bool IsExtern,
+                                               const Expr *Init) {
   // Create a descriptor for the global.
   Descriptor *Desc;
   const bool IsConst = Ty.isConstQualified();
@@ -183,19 +185,17 @@
   if (auto T = Ctx.classify(Ty)) {
     Desc = createDescriptor(D, *T, IsConst, IsTemporary);
   } else {
-    Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary);
+    Desc =
+        createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary, false, Init);
   }
   if (!Desc)
     return {};
 
   // Allocate a block for storage.
   unsigned I = Globals.size();
-
-  auto *G = new (Allocator, Desc->getAllocSize())
-      Global(getCurrentDecl(), Desc, IsStatic, IsExtern);
-  G->block()->invokeCtor();
-
-  Globals.push_back(G);
+  auto *G = allocateBlock(Desc, getCurrentDecl(), Desc, IsStatic, IsExtern);
+  G->invokeCtor();
+  Globals.emplace_back(G);
 
   return I;
 }
@@ -310,7 +310,7 @@
 
 Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
                                       bool IsConst, bool IsTemporary,
-                                      bool IsMutable) {
+                                      bool IsMutable, const Expr *Init) {
   // Classes and structures.
   if (auto *RT = Ty->getAs<RecordType>()) {
     if (auto *Record = getOrCreateRecord(RT->getDecl()))
@@ -322,20 +322,34 @@
     QualType ElemTy = ArrayType->getElementType();
     // Array of well-known bounds.
     if (auto CAT = dyn_cast<ConstantArrayType>(ArrayType)) {
-      size_t NumElems = CAT->getSize().getZExtValue();
+      const size_t NumElems = CAT->getSize().getZExtValue();
+      size_t NumGivenElems = NumElems;
+
+      const Expr *ArrayFiller = nullptr;
+      const auto *InitList = dyn_cast_if_present<InitListExpr>(Init);
+      if (InitList && InitList->hasArrayFiller()) {
+        ArrayFiller = InitList->getArrayFiller();
+
+        NumGivenElems = InitList->getNumInits();
+        assert(NumGivenElems < NumElems);
+        // TODO: Check if the array filler is element-dependent.
+        //   We can just copy that from ExprConstant.cpp \o/
+        // TODO: Is this really only for primitive arrays?
+      }
+
       if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) {
         // Arrays of primitives.
         unsigned ElemSize = primSize(*T);
-        if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) {
+        if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems)
           return {};
-        }
-        return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary,
-                                  IsMutable);
+        return allocateDescriptor(D, *T, NumGivenElems, ArrayFiller, NumElems,
+                                  IsConst, IsTemporary, IsMutable);
       } else {
         // Arrays of composites. In this case, the array is a list of pointers,
         // followed by the actual elements.
         Descriptor *ElemDesc =
             createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary);
+
         if (!ElemDesc)
           return nullptr;
         InterpSize ElemSize =
@@ -373,7 +387,8 @@
   // Complex types - represented as arrays of elements.
   if (auto *CT = Ty->getAs<ComplexType>()) {
     PrimType ElemTy = *Ctx.classify(CT->getElementType());
-    return allocateDescriptor(D, ElemTy, 2, IsConst, IsTemporary, IsMutable);
+    return allocateDescriptor(D, ElemTy, 2, nullptr, 2, IsConst, IsTemporary,
+                              IsMutable);
   }
 
   return nullptr;
Index: clang/lib/AST/Interp/Pointer.h
===================================================================
--- clang/lib/AST/Interp/Pointer.h
+++ clang/lib/AST/Interp/Pointer.h
@@ -55,6 +55,7 @@
   Pointer atIndex(unsigned Idx) const {
     if (Base == RootPtrMark)
       return Pointer(Pointee, RootPtrMark, getDeclDesc()->getSize());
+
     unsigned Off = Idx * elemSize();
     if (getFieldDesc()->ElemDesc)
       Off += sizeof(InlineDescriptor);
@@ -213,6 +214,8 @@
   bool isRoot() const {
     return (Base == 0 || Base == RootPtrMark) && Offset == 0;
   }
+  /// Returns the Block this pointer points to.
+  Block *block() const { return Pointee; }
 
   /// Returns the record descriptor of a class.
   Record *getRecord() const { return getFieldDesc()->ElemRecord; }
@@ -240,6 +243,19 @@
   /// Checks if a structure is a base class.
   bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
 
+  const Expr *getArrayFiller() const {
+    return getFieldDesc()->getArrayFiller();
+  }
+
+  bool needsExpansionForIndex(unsigned Idx) const {
+    const Descriptor *Desc = getFieldDesc();
+    // The given index should never be greater than NumFullElems,
+    // this needs to be checked earlier.
+    assert(Idx < Desc->getNumFullElems());
+    return Desc->isArray() &&
+      Idx >= Desc->getNumElems();
+  }
+
   /// Checks if an object or a subfield is mutable.
   bool isConst() const {
     return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
@@ -256,6 +272,9 @@
   /// Returns the number of elements.
   unsigned getNumElems() const { return getSize() / elemSize(); }
 
+  /// Returns the maximum number of elements.
+  unsigned getNumFullElems() const { return getFieldDesc()->getNumFullElems(); }
+
   /// Returns the index into an array.
   int64_t getIndex() const {
     if (isElementPastEnd())
Index: clang/lib/AST/Interp/Pointer.cpp
===================================================================
--- clang/lib/AST/Interp/Pointer.cpp
+++ clang/lib/AST/Interp/Pointer.cpp
@@ -10,18 +10,28 @@
 #include "Function.h"
 #include "InterpBlock.h"
 #include "PrimType.h"
+#include "Program.h"
 
 using namespace clang;
 using namespace clang::interp;
 
-Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
+Pointer::Pointer(Block *Pointee) : Pointee(Pointee), Base(0), Offset(0) {
+  if (Pointee)
+    Pointee->addPointer(this);
+}
 
-Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
+Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {
+  if (Pointee)
+    assert(Pointee->hasPointer(this));
+}
 
 Pointer::Pointer(Pointer &&P)
     : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) {
-  if (Pointee)
-    Pointee->movePointer(&P, this);
+  if (Pointee) {
+    assert(!Pointee->hasPointer(this));
+    Pointee->addPointer(this);
+    assert(Pointee->hasPointer(this));
+  }
 }
 
 Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset)
@@ -33,8 +43,9 @@
 
 Pointer::~Pointer() {
   if (Pointee) {
-    Pointee->removePointer(this);
     Pointee->cleanup();
+    Pointee->removePointer(this);
+    Pointee = nullptr;
   }
 }
 
@@ -48,9 +59,12 @@
   Base = P.Base;
 
   Pointee = P.Pointee;
-  if (Pointee)
+  if (Pointee) {
     Pointee->addPointer(this);
+    assert(Pointee->hasPointer(this));
+  }
 
+  assert(Pointee->hasPointer(this));
   if (Old)
     Old->cleanup();
 }
@@ -58,15 +72,17 @@
 void Pointer::operator=(Pointer &&P) {
   Block *Old = Pointee;
 
-  if (Pointee)
+  if (this->Pointee)
     Pointee->removePointer(this);
 
   Offset = P.Offset;
   Base = P.Base;
-
   Pointee = P.Pointee;
-  if (Pointee)
-    Pointee->movePointer(&P, this);
+
+  if (Pointee) {
+    Pointee->addPointer(this);
+    assert(Pointee->hasPointer(this));
+  }
 
   if (Old)
     Old->cleanup();
Index: clang/lib/AST/Interp/InterpStack.h
===================================================================
--- clang/lib/AST/Interp/InterpStack.h
+++ clang/lib/AST/Interp/InterpStack.h
@@ -61,6 +61,9 @@
   /// Clears the stack without calling any destructors.
   void clear();
 
+  /// Returns true if the stack is empty.
+  bool empty() const { return size() == 0; }
+
 private:
   /// All stack slots are aligned to the native pointer alignment for storage.
   /// The size of an object is rounded up to a pointer alignment multiple.
Index: clang/lib/AST/Interp/InterpFrame.h
===================================================================
--- clang/lib/AST/Interp/InterpFrame.h
+++ clang/lib/AST/Interp/InterpFrame.h
@@ -14,7 +14,6 @@
 #define LLVM_CLANG_AST_INTERP_INTERPFRAME_H
 
 #include "Frame.h"
-#include "Pointer.h"
 #include "Program.h"
 #include "State.h"
 #include <cstdint>
@@ -24,6 +23,7 @@
 namespace interp {
 class Function;
 class InterpState;
+class Pointer;
 
 /// Frame storing local variables.
 class InterpFrame final : public Frame {
@@ -142,6 +142,8 @@
   char *Args = nullptr;
   /// Fixed, initial storage for known local variables.
   std::unique_ptr<char[]> Locals;
+  /// Pointers we lazily create for the locals.
+  llvm::DenseMap<unsigned, Pointer> LocalPointers;
   /// Offset on the stack at entry.
   const size_t FrameOffset;
   /// Mapping from arg offsets to their argument blocks.
Index: clang/lib/AST/Interp/InterpFrame.cpp
===================================================================
--- clang/lib/AST/Interp/InterpFrame.cpp
+++ clang/lib/AST/Interp/InterpFrame.cpp
@@ -10,6 +10,7 @@
 #include "Function.h"
 #include "Interp.h"
 #include "InterpStack.h"
+#include "Pointer.h"
 #include "PrimType.h"
 #include "Program.h"
 #include "clang/AST/DeclCXX.h"
@@ -154,8 +155,13 @@
 
 Pointer InterpFrame::getLocalPointer(unsigned Offset) {
   assert(Offset < Func->getFrameSize() && "Invalid local offset.");
-  return Pointer(
-      reinterpret_cast<Block *>(Locals.get() + Offset - sizeof(Block)));
+  if (auto It = LocalPointers.find(Offset); It != LocalPointers.end())
+    return Pointer(It->second);
+
+  auto *B = reinterpret_cast<Block *>(Locals.get() + Offset - sizeof(Block));
+  Pointer &P = LocalPointers[Offset];
+  P = Pointer(B);
+  return P;
 }
 
 Pointer InterpFrame::getParamPointer(unsigned Off) {
Index: clang/lib/AST/Interp/InterpBlock.h
===================================================================
--- clang/lib/AST/Interp/InterpBlock.h
+++ clang/lib/AST/Interp/InterpBlock.h
@@ -60,6 +60,7 @@
 
   /// Returns a pointer to the stored data.
   char *data() { return reinterpret_cast<char *>(this + 1); }
+  const char *data() const { return reinterpret_cast<const char *>(this + 1); }
 
   /// Returns a view over the data.
   template <typename T>
@@ -73,6 +74,17 @@
                    /*isActive=*/true, Desc);
   }
 
+  /// Checks if \P is in the list of pointers this block manages.
+  /// This does NOT check if P->Pointee is this block.
+  ///
+  /// Only use this function in assertions.
+  bool hasPointer(const Pointer *P) const;
+
+  /// Transfer all pointers pointing to this block to \t To.
+  /// After calling this function, this->hasPointers() will
+  /// return false.
+  void transferPointersTo(Block *To);
+
 protected:
   friend class Pointer;
   friend class DeadBlock;
@@ -87,7 +99,6 @@
   // Pointer chain management.
   void addPointer(Pointer *P);
   void removePointer(Pointer *P);
-  void movePointer(Pointer *From, Pointer *To);
 
   /// Start of the chain of pointers.
   Pointer *Pointers = nullptr;
Index: clang/lib/AST/Interp/InterpBlock.cpp
===================================================================
--- clang/lib/AST/Interp/InterpBlock.cpp
+++ clang/lib/AST/Interp/InterpBlock.cpp
@@ -16,11 +16,11 @@
 using namespace clang;
 using namespace clang::interp;
 
-
-
 void Block::addPointer(Pointer *P) {
-  if (IsStatic)
-    return;
+  assert(Pointers != P);
+  assert(P);
+  assert(!hasPointer(P));
+
   if (Pointers)
     Pointers->Prev = P;
   P->Next = Pointers;
@@ -29,14 +29,17 @@
 }
 
 void Block::removePointer(Pointer *P) {
-  if (IsStatic)
-    return;
+  assert(P);
+
   if (Pointers == P)
     Pointers = P->Next;
   if (P->Prev)
     P->Prev->Next = P->Next;
   if (P->Next)
     P->Next->Prev = P->Prev;
+  // Setting P->Pointee to null sounds like a good idea but
+  // it seems like some things (e.g. the Pointee->cleanup() call
+  // in ~Pointer()) rely on this not happening.
 }
 
 void Block::cleanup() {
@@ -44,20 +47,35 @@
     (reinterpret_cast<DeadBlock *>(this + 1) - 1)->free();
 }
 
-void Block::movePointer(Pointer *From, Pointer *To) {
-  if (IsStatic)
+void Block::transferPointersTo(Block *To) {
+  assert(To);
+  if (!Pointers)
     return;
-  To->Prev = From->Prev;
-  if (To->Prev)
-    To->Prev->Next = To;
-  To->Next = From->Next;
-  if (To->Next)
-    To->Next->Prev = To;
-  if (Pointers == From)
-    Pointers = To;
-
-  From->Prev = nullptr;
-  From->Next = nullptr;
+
+  Pointer *P = this->Pointers;
+  while (P) {
+    Pointer *Next = P->Next;
+    removePointer(P);
+    To->addPointer(P);
+    P->Pointee = To;
+
+    P = Next;
+  }
+  assert(!hasPointers());
+}
+
+bool Block::hasPointer(const Pointer *P) const {
+  const Pointer *Iter = this->Pointers;
+  while (Iter) {
+    if (Iter == P)
+      return true;
+
+    assert(Iter != P);
+    assert(Iter != Iter->Next);
+    Iter = Iter->Next;
+  }
+
+  return false;
 }
 
 DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
Index: clang/lib/AST/Interp/Interp.h
===================================================================
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -30,7 +30,6 @@
 #include "llvm/Support/Endian.h"
 #include <limits>
 #include <type_traits>
-#include <vector>
 
 namespace clang {
 namespace interp {
@@ -732,6 +731,7 @@
     return false;
   Ptr.initialize();
   new (&Ptr.deref<T>()) T(Value);
+
   return true;
 }
 
@@ -773,7 +773,7 @@
     return false;
 
   // Compute the largest index into the array.
-  unsigned MaxIndex = Ptr.getNumElems();
+  unsigned MaxIndex = Ptr.getNumFullElems();
 
   // Helper to report an invalid offset, computed as APSInt.
   auto InvalidOffset = [&]() {
@@ -795,7 +795,7 @@
       return InvalidOffset();
 
     // If the new offset would be out of bounds, bail out.
-    if (Offset.isPositive() && Offset > MaxOffset)
+    if (Offset.isPositive() && Offset >= MaxOffset)
       return InvalidOffset();
   } else {
     // If the new offset would be negative, bail out.
@@ -811,7 +811,34 @@
   int64_t WideIndex = static_cast<int64_t>(Index);
   int64_t WideOffset = static_cast<int64_t>(Offset);
   int64_t Result = Add ? (WideIndex + WideOffset) : (WideIndex - WideOffset);
-  S.Stk.push<Pointer>(Ptr.atIndex(static_cast<unsigned>(Result)));
+  unsigned UResult = static_cast<unsigned>(Result);
+
+  // For arrays, we might need to expand them using the filler element.
+  if (Ptr.needsExpansionForIndex(UResult)) {
+    Block *Pointee = Ptr.block();
+    const Descriptor *Desc = Ptr.getFieldDesc();
+    unsigned OldNumElems = Desc->getNumElems();
+    unsigned NewNumElems = std::min(UResult * 2, Desc->getNumFullElems());
+    assert(NewNumElems > OldNumElems);
+
+    // TODO: Just create a different Descriptor constructor for this case
+    Descriptor *D = new Descriptor(
+        Desc->asDecl(), static_cast<PrimType>(4), // PT_Sint32,
+        NewNumElems, Desc->getArrayFiller(), Desc->getNumFullElems(),
+        Desc->IsConst, Desc->IsTemporary, Desc->IsMutable);
+    char *m = (char *)malloc(sizeof(Block) + D->getAllocSize());
+    Block *_B = new (m) Block(D, Pointee->isStatic(), Pointee->isExtern());
+    _B->invokeCtor();
+
+    memcpy(_B->data(), Pointee->data(), Desc->getAllocSize());
+
+    Pointee->transferPointersTo(_B);
+    assert(Ptr.block() == _B);
+    assert(Ptr.block()->hasPointer(&Ptr));
+    // TODO:  Actually use the array filler for the new elements!
+  }
+
+  S.Stk.push<Pointer>(Ptr.atIndex(UResult));
   return true;
 }
 
Index: clang/lib/AST/Interp/Integral.h
===================================================================
--- clang/lib/AST/Interp/Integral.h
+++ clang/lib/AST/Interp/Integral.h
@@ -90,6 +90,9 @@
   bool operator>(unsigned RHS) const {
     return V >= 0 && static_cast<unsigned>(V) > RHS;
   }
+  bool operator>=(unsigned RHS) const {
+    return V >= 0 && static_cast<unsigned>(V) >= RHS;
+  }
 
   Integral operator-() const { return Integral(-V); }
   Integral operator~() const { return Integral(~V); }
Index: clang/lib/AST/Interp/EvalEmitter.cpp
===================================================================
--- clang/lib/AST/Interp/EvalEmitter.cpp
+++ clang/lib/AST/Interp/EvalEmitter.cpp
@@ -186,10 +186,13 @@
       }
       return Ok;
     }
-    if (auto *AT = Ty->getAsArrayTypeUnsafe()) {
-      const size_t NumElems = Ptr.getNumElems();
+    if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
+      size_t NumElems = Ptr.getNumElems();
+      const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT);
+      const size_t FullElems = CAT->getSize().getZExtValue();
+
       QualType ElemTy = AT->getElementType();
-      R = APValue(APValue::UninitArray{}, NumElems, NumElems);
+      R = APValue(APValue::UninitArray{}, NumElems, FullElems);
 
       bool Ok = true;
       for (unsigned I = 0; I < NumElems; ++I) {
@@ -201,6 +204,22 @@
           Ok &= Composite(ElemTy, EP.narrow(), Slot);
         }
       }
+
+      if (NumElems != FullElems) {
+        // Need to convert the array filler to a APValue
+        const Expr *AF = Ptr.getArrayFiller();
+        assert(AF);
+
+        // TODO: I have no idea if the potentially nested call
+        //  to evaluateAsRValue() here is always going to work.
+        //
+        //  We should evaluate the array filler *once* during
+        //  normal evaluation, not when returning the value.
+        APValue FillerValue;
+        Ctx.evaluateAsRValue(S, AF, FillerValue);
+        FillerValue.dump();
+        R.getArrayFiller() = FillerValue;
+      }
       return Ok;
     }
     llvm_unreachable("invalid value to return");
Index: clang/lib/AST/Interp/Descriptor.h
===================================================================
--- clang/lib/AST/Interp/Descriptor.h
+++ clang/lib/AST/Interp/Descriptor.h
@@ -78,6 +78,10 @@
   const bool IsTemporary = false;
   /// Flag indicating if the block is an array.
   const bool IsArray = false;
+  /// NB: Might be different than (Size / ElemSize), for arrays
+  ///   that have an array filler.
+  const InterpSize NumFullElems = 0;
+  const Expr *ArrayFiller = nullptr;
 
   /// Storage management methods.
   const BlockCtorFn CtorFn = nullptr;
@@ -89,7 +93,8 @@
              bool IsMutable);
 
   /// Allocates a descriptor for an array of primitives.
-  Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, bool IsConst,
+  Descriptor(const DeclTy &D, PrimType Type, size_t NumElems,
+             const Expr *Filler, size_t FullElems, bool IsConst,
              bool IsTemporary, bool IsMutable);
 
   /// Allocates a descriptor for an array of primitives of unknown size.
@@ -111,6 +116,7 @@
 
   const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); }
   const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); }
+  const Expr *getArrayFiller() const { return ArrayFiller; }
 
   const ValueDecl *asValueDecl() const {
     return dyn_cast_or_null<ValueDecl>(asDecl());
@@ -139,6 +145,10 @@
   unsigned getNumElems() const {
     return Size == UnknownSizeMark ? 0 : (getSize() / getElemSize());
   }
+  /// Returns the maximum number the block may hold.
+  unsigned getNumFullElems() const {
+    return Size == UnknownSizeMark ? 0 : NumFullElems;
+  }
 
   /// Checks if the descriptor is of an array of primitives.
   bool isPrimitiveArray() const { return IsArray && !ElemDesc; }
Index: clang/lib/AST/Interp/Descriptor.cpp
===================================================================
--- clang/lib/AST/Interp/Descriptor.cpp
+++ clang/lib/AST/Interp/Descriptor.cpp
@@ -194,14 +194,19 @@
   assert(Source && "Missing source");
 }
 
+/// XXX Just make a new constructor that takes 2 sizes as well as an array
+/// filler expression.
 Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems,
-                       bool IsConst, bool IsTemporary, bool IsMutable)
+                       const Expr *Filler, size_t FullElems, bool IsConst,
+                       bool IsTemporary, bool IsMutable)
     : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
       AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst),
       IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
+      NumFullElems(FullElems), ArrayFiller(Filler),
       CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
       MoveFn(getMoveArrayPrim(Type)) {
   assert(Source && "Missing source");
+  assert(NumElems <= FullElems);
 }
 
 Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
@@ -219,8 +224,8 @@
       Size(ElemSize * NumElems),
       AllocSize(std::max<size_t>(alignof(void *), Size)), ElemDesc(Elem),
       IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
-      IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc),
-      MoveFn(moveArrayDesc) {
+      IsArray(true), NumFullElems(NumElems), CtorFn(ctorArrayDesc),
+      DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
   assert(Source && "Missing source");
 }
 
Index: clang/lib/AST/Interp/Context.cpp
===================================================================
--- clang/lib/AST/Interp/Context.cpp
+++ clang/lib/AST/Interp/Context.cpp
@@ -27,6 +27,7 @@
 Context::~Context() {}
 
 bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
+  assert(Stk.empty());
   Function *Func = P->getFunction(FD);
   if (!Func) {
     if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD)) {
@@ -39,22 +40,45 @@
     }
   }
 
+  Stk.clear();
+
   if (!Func->isConstexpr())
     return false;
 
-  APValue Dummy;
-  return Run(Parent, Func, Dummy);
+  // XXX We *actually* just ONLY want to assert that the stack is empty.
+  //   (Without the clear() above).
+  //   However, currently we emit some unnecessary Destroy() and RetVoid
+  //   ops. Long-term, we should get rid of those. But short-term, they
+  //   cause no harm.
+  assert(Stk.empty());
+  return true;
 }
 
 bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
+  assert(Stk.empty());
   ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
-  return Check(Parent, C.interpretExpr(E));
+
+  if (Check(Parent, C.interpretExpr(E))) {
+    assert(Stk.empty());
+    return true;
+  }
+
+  Stk.clear();
+  return false;
 }
 
 bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
                                     APValue &Result) {
+  assert(Stk.empty());
   ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
-  return Check(Parent, C.interpretDecl(VD));
+
+  if (Check(Parent, C.interpretDecl(VD))) {
+    assert(Stk.empty());
+    return true;
+  }
+
+  Stk.clear();
+  return false;
 }
 
 const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); }
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -513,18 +513,22 @@
   QualType Ty;
 
   const ValueDecl *Key = nullptr;
+  const Expr *Init = nullptr;
   bool IsTemporary = false;
-  if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) {
+  if (auto *VD = dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) {
     Key = VD;
     Ty = VD->getType();
+
+    if (const auto *VarD = dyn_cast<VarDecl>(VD))
+      Init = VarD->getInit();
   }
   if (auto *E = Src.dyn_cast<const Expr *>()) {
     IsTemporary = true;
     Ty = E->getType();
   }
 
-  Descriptor *D = P.createDescriptor(Src, Ty.getTypePtr(),
-                                     Ty.isConstQualified(), IsTemporary);
+  Descriptor *D = P.createDescriptor(
+      Src, Ty.getTypePtr(), Ty.isConstQualified(), IsTemporary, false, Init);
   if (!D)
     return {};
 
@@ -541,7 +545,6 @@
 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()) {
@@ -657,7 +660,7 @@
 bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) {
   const Expr *Init = VD->getInit();
 
-  if (Optional<unsigned> I = P.createGlobal(VD)) {
+  if (Optional<unsigned> I = P.createGlobal(VD, Init)) {
     if (Optional<PrimType> T = classify(VD->getType())) {
       {
         // Primitive declarations - compute the value and set it.
@@ -683,6 +686,7 @@
       // Return a pointer to the global.
       if (!this->emitGetPtrGlobal(*I, VD))
         return false;
+
       return this->emitRetValue(VD);
     }
   }
Index: clang/lib/AST/Interp/Boolean.h
===================================================================
--- clang/lib/AST/Interp/Boolean.h
+++ clang/lib/AST/Interp/Boolean.h
@@ -42,6 +42,7 @@
   bool operator!=(Boolean RHS) const { return V != RHS.V; }
 
   bool operator>(unsigned RHS) const { return static_cast<unsigned>(V) > RHS; }
+  bool operator>=(unsigned RHS) const { return static_cast<unsigned>(V) >= RHS; }
 
   Boolean operator-() const { return Boolean(V); }
   Boolean operator~() const { return Boolean(true); }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to