Commit 3fe2be75 authored by Timm Bäder's avatar Timm Bäder
Browse files

[clang][Interp] Call destructors of local variables

Differential Revision: https://reviews.llvm.org/D154581
parent 14b039c1
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -160,8 +160,16 @@ const llvm::fltSemantics &Context::getFloatSemantics(QualType T) const {
bool Context::Run(State &Parent, const Function *Func, APValue &Result) {
  InterpState State(Parent, *P, Stk, *this);
  State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, {});
  if (Interpret(State, Result))
  if (Interpret(State, Result)) {
    assert(Stk.empty());
    return true;
  }

  // We explicitly delete our state here, so the Stk.clear() call
  // below doesn't violently free values the destructor would
  // otherwise access.
  State.~InterpState();

  Stk.clear();
  return false;
}
+25 −34
Original line number Diff line number Diff line
@@ -40,6 +40,9 @@ static void moveTy(Block *, const std::byte *Src, std::byte *Dst,
template <typename T>
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool,
                        const Descriptor *D) {
  new (Ptr) InitMapPtr(std::nullopt);

  Ptr += sizeof(InitMapPtr);
  for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
    new (&reinterpret_cast<T *>(Ptr)[I]) T();
  }
@@ -47,11 +50,11 @@ static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool,

template <typename T>
static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) {
  InitMap *IM = *reinterpret_cast<InitMap **>(Ptr);
  if (IM != (InitMap *)-1)
    free(IM);
  InitMapPtr &IMP = *reinterpret_cast<InitMapPtr *>(Ptr);

  Ptr += sizeof(InitMap *);
  if (IMP)
    IMP = std::nullopt;
  Ptr += sizeof(InitMapPtr);
  for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
    reinterpret_cast<T *>(Ptr)[I].~T();
  }
@@ -209,7 +212,8 @@ static BlockMoveFn getMovePrim(PrimType Type) {
}

static BlockCtorFn getCtorArrayPrim(PrimType Type) {
  COMPOSITE_TYPE_SWITCH(Type, return ctorArrayTy<T>, return nullptr);
  TYPE_SWITCH(Type, return ctorArrayTy<T>);
  llvm_unreachable("unknown Expr");
}

static BlockDtorFn getDtorArrayPrim(PrimType Type) {
@@ -218,7 +222,8 @@ static BlockDtorFn getDtorArrayPrim(PrimType Type) {
}

static BlockMoveFn getMoveArrayPrim(PrimType Type) {
  COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, return nullptr);
  TYPE_SWITCH(Type, return moveArrayTy<T>);
  llvm_unreachable("unknown Expr");
}

/// Primitives.
@@ -238,7 +243,7 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
                       bool IsMutable)
    : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
      MDSize(MD.value_or(0)),
      AllocSize(align(Size) + sizeof(InitMap *) + MDSize), IsConst(IsConst),
      AllocSize(align(Size) + sizeof(InitMapPtr) + MDSize), IsConst(IsConst),
      IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
      CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
      MoveFn(getMoveArrayPrim(Type)) {
@@ -249,9 +254,10 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
                       UnknownSize)
    : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0),
      AllocSize(alignof(void *)), IsConst(true), IsMutable(false),
      IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)),
      DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
      AllocSize(alignof(void *) + sizeof(InitMapPtr)), IsConst(true),
      IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
      CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
      MoveFn(getMoveArrayPrim(Type)) {
  assert(Source && "Missing source");
}

@@ -272,10 +278,10 @@ Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary,
                       UnknownSize)
    : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
      Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)),
      ElemDesc(Elem), IsConst(true), IsMutable(false), IsTemporary(IsTemporary),
      IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc),
      MoveFn(moveArrayDesc) {
      Size(UnknownSizeMark), MDSize(0),
      AllocSize(alignof(void *) + sizeof(InitMapPtr)), ElemDesc(Elem),
      IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
      CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
  assert(Source && "Missing source");
}

@@ -314,21 +320,12 @@ SourceLocation Descriptor::getLocation() const {
  llvm_unreachable("Invalid descriptor type");
}

InitMap::InitMap(unsigned N) : UninitFields(N) {
  std::fill_n(data(), (N + PER_FIELD - 1) / PER_FIELD, 0);
InitMap::InitMap(unsigned N)
    : UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {
  std::fill_n(data(), numFields(N), 0);
}

InitMap::T *InitMap::data() {
  auto *Start = reinterpret_cast<char *>(this) + align(sizeof(InitMap));
  return reinterpret_cast<T *>(Start);
}

const InitMap::T *InitMap::data() const {
  auto *Start = reinterpret_cast<const char *>(this) + align(sizeof(InitMap));
  return reinterpret_cast<const T *>(Start);
}

bool InitMap::initialize(unsigned I) {
bool InitMap::initializeElement(unsigned I) {
  unsigned Bucket = I / PER_FIELD;
  T Mask = T(1) << (I % PER_FIELD);
  if (!(data()[Bucket] & Mask)) {
@@ -338,13 +335,7 @@ bool InitMap::initialize(unsigned I) {
  return UninitFields == 0;
}

bool InitMap::isInitialized(unsigned I) const {
bool InitMap::isElementInitialized(unsigned I) const {
  unsigned Bucket = I / PER_FIELD;
  return data()[Bucket] & (T(1) << (I % PER_FIELD));
}

InitMap *InitMap::allocate(unsigned N) {
  const size_t NumFields = ((N + PER_FIELD - 1) / PER_FIELD);
  const size_t Size = align(sizeof(InitMap)) + NumFields * PER_FIELD;
  return new (malloc(Size)) InitMap(N);
}
+16 −14
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@ namespace clang {
namespace interp {
class Block;
class Record;
struct InitMap;
struct Descriptor;
enum PrimType : unsigned;

using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
using InitMapPtr = std::optional<std::pair<bool, std::shared_ptr<InitMap>>>;

/// Invoked whenever a block is created. The constructor method fills in the
/// inline descriptors of all fields and array elements. It also initializes
@@ -193,9 +195,6 @@ public:
};

/// Bitfield tracking the initialisation status of elements of primitive arrays.
/// A pointer to this is embedded at the end of all primitive arrays.
/// If the map was not yet created and nothing was initialized, the pointer to
/// this structure is 0. If the object was fully initialized, the pointer is -1.
struct InitMap final {
private:
  /// Type packing bits.
@@ -203,26 +202,29 @@ private:
  /// Bits stored in a single field.
  static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT;

public:
  /// Initializes the map with no fields set.
  InitMap(unsigned N);
  explicit InitMap(unsigned N);

private:
  friend class Pointer;

  /// Returns a pointer to storage.
  T *data();
  const T *data() const;
  T *data() { return Data.get(); }
  const T *data() const { return Data.get(); }

public:
  /// Initializes an element. Returns true when object if fully initialized.
  bool initialize(unsigned I);
  bool initializeElement(unsigned I);

  /// Checks if an element was initialized.
  bool isInitialized(unsigned I) const;
  bool isElementInitialized(unsigned I) const;

  /// Allocates a map holding N elements.
  static InitMap *allocate(unsigned N);

private:
  /// Number of fields initialized.
  static constexpr size_t numFields(unsigned N) {
    return (N + PER_FIELD - 1) / PER_FIELD;
  }
  /// Number of fields not initialized.
  unsigned UninitFields;
  std::unique_ptr<T[]> Data;
};

} // namespace interp
+8 −0
Original line number Diff line number Diff line
@@ -25,6 +25,14 @@ EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
      new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr());
}

EvalEmitter::~EvalEmitter() {
  for (auto &[K, V] : Locals) {
    Block *B = reinterpret_cast<Block *>(V.get());
    if (B->isInitialized())
      B->invokeDtor();
  }
}

llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) {
  if (this->visitExpr(E))
    return true;
+1 −1
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ protected:
  EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk,
              APValue &Result);

  virtual ~EvalEmitter() {}
  virtual ~EvalEmitter();

  /// Define a label.
  void emitLabel(LabelTy Label);
Loading