js/src/frontend/NameCollections.h
author Tooru Fujisawa <arai_a@mac.com>
Wed, 13 Mar 2019 04:29:44 +0000
changeset 521653 123ecc5c9586791c2ebae62c4a4e3cb600274975
parent 505471 66eb1f485c1a3ea81372758bc92292c9428b17cd
permissions -rw-r--r--
Bug 1505343 - Part 1: Rename binsource => binast. r=Yoric Differential Revision: https://phabricator.services.mozilla.com/D23097

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef frontend_NameCollections_h
#define frontend_NameCollections_h

#include "ds/InlineTable.h"
#include "frontend/NameAnalysisTypes.h"
#include "js/Vector.h"

namespace js {
namespace frontend {

class FunctionBox;

// A pool of recyclable containers for use in the frontend. The Parser and
// BytecodeEmitter create many maps for name analysis that are short-lived
// (i.e., for the duration of parsing or emitting a lexical scope). Making
// them recyclable cuts down significantly on allocator churn.
template <typename RepresentativeCollection, typename ConcreteCollectionPool>
class CollectionPool {
  using RecyclableCollections = Vector<void*, 32, SystemAllocPolicy>;

  RecyclableCollections all_;
  RecyclableCollections recyclable_;

  static RepresentativeCollection* asRepresentative(void* p) {
    return reinterpret_cast<RepresentativeCollection*>(p);
  }

  RepresentativeCollection* allocate() {
    size_t newAllLength = all_.length() + 1;
    if (!all_.reserve(newAllLength) || !recyclable_.reserve(newAllLength)) {
      return nullptr;
    }

    RepresentativeCollection* collection = js_new<RepresentativeCollection>();
    if (collection) {
      all_.infallibleAppend(collection);
    }
    return collection;
  }

 public:
  ~CollectionPool() { purgeAll(); }

  void purgeAll() {
    void** end = all_.end();
    for (void** it = all_.begin(); it != end; ++it) {
      js_delete(asRepresentative(*it));
    }

    all_.clearAndFree();
    recyclable_.clearAndFree();
  }

  // Fallibly aquire one of the supported collection types from the pool.
  template <typename Collection>
  Collection* acquire(JSContext* cx) {
    ConcreteCollectionPool::template assertInvariants<Collection>();

    RepresentativeCollection* collection;
    if (recyclable_.empty()) {
      collection = allocate();
      if (!collection) {
        ReportOutOfMemory(cx);
      }
    } else {
      collection = asRepresentative(recyclable_.popCopy());
      collection->clear();
    }
    return reinterpret_cast<Collection*>(collection);
  }

  // Release a collection back to the pool.
  template <typename Collection>
  void release(Collection** collection) {
    ConcreteCollectionPool::template assertInvariants<Collection>();
    MOZ_ASSERT(*collection);

#ifdef DEBUG
    bool ok = false;
    // Make sure the collection is in |all_| but not already in |recyclable_|.
    for (void** it = all_.begin(); it != all_.end(); ++it) {
      if (*it == *collection) {
        ok = true;
        break;
      }
    }
    MOZ_ASSERT(ok);
    for (void** it = recyclable_.begin(); it != recyclable_.end(); ++it) {
      MOZ_ASSERT(*it != *collection);
    }
#endif

    MOZ_ASSERT(recyclable_.length() < all_.length());
    // Reserved in allocateFresh.
    recyclable_.infallibleAppend(*collection);
    *collection = nullptr;
  }
};

template <typename Wrapped>
struct RecyclableAtomMapValueWrapper {
  union {
    Wrapped wrapped;
    uint64_t dummy;
  };

  static void assertInvariant() {
    static_assert(sizeof(Wrapped) <= sizeof(uint64_t),
                  "Can only recycle atom maps with values smaller than uint64");
  }

  RecyclableAtomMapValueWrapper() : dummy(0) { assertInvariant(); }

  MOZ_IMPLICIT RecyclableAtomMapValueWrapper(Wrapped w) : wrapped(w) {
    assertInvariant();
  }

  MOZ_IMPLICIT operator Wrapped&() { return wrapped; }

  MOZ_IMPLICIT operator Wrapped&() const { return wrapped; }

  Wrapped* operator->() { return &wrapped; }

  const Wrapped* operator->() const { return &wrapped; }
};

struct NameMapHasher : public DefaultHasher<JSAtom*> {
  static inline HashNumber hash(const Lookup& l) {
    // Name maps use the atom's precomputed hash code, which is based on
    // the atom's contents rather than its pointer value. This is necessary
    // to preserve iteration order while recording/replaying: iteration can
    // affect generated script bytecode and the order in which e.g. lookup
    // property hooks are performed on the associated global.
    return l->hash();
  }
};

template <typename MapValue>
using RecyclableNameMap =
    InlineMap<JSAtom*, RecyclableAtomMapValueWrapper<MapValue>, 24,
              NameMapHasher, SystemAllocPolicy>;

using DeclaredNameMap = RecyclableNameMap<DeclaredNameInfo>;
using NameLocationMap = RecyclableNameMap<NameLocation>;
using AtomIndexMap = RecyclableNameMap<uint32_t>;

template <typename RepresentativeTable>
class InlineTablePool
    : public CollectionPool<RepresentativeTable,
                            InlineTablePool<RepresentativeTable>> {
 public:
  template <typename Table>
  static void assertInvariants() {
    static_assert(
        Table::SizeOfInlineEntries == RepresentativeTable::SizeOfInlineEntries,
        "Only tables with the same size for inline entries are usable in the "
        "pool.");
    static_assert(mozilla::IsPod<typename Table::Table::Entry>::value,
                  "Only tables with POD values are usable in the pool.");
  }
};

using FunctionBoxVector = Vector<FunctionBox*, 24, SystemAllocPolicy>;

template <typename RepresentativeVector>
class VectorPool : public CollectionPool<RepresentativeVector,
                                         VectorPool<RepresentativeVector>> {
 public:
  template <typename Vector>
  static void assertInvariants() {
    static_assert(
        Vector::sMaxInlineStorage == RepresentativeVector::sMaxInlineStorage,
        "Only vectors with the same size for inline entries are usable in the "
        "pool.");
    static_assert(mozilla::IsPod<typename Vector::ElementType>::value,
                  "Only vectors of POD values are usable in the pool.");
    static_assert(
        sizeof(typename Vector::ElementType) ==
            sizeof(typename RepresentativeVector::ElementType),
        "Only vectors with same-sized elements are usable in the pool.");
  }
};

class NameCollectionPool {
  InlineTablePool<AtomIndexMap> mapPool_;
  VectorPool<AtomVector> vectorPool_;
  uint32_t activeCompilations_;

 public:
  NameCollectionPool() : activeCompilations_(0) {}

  bool hasActiveCompilation() const { return activeCompilations_ != 0; }

  void addActiveCompilation() { activeCompilations_++; }

  void removeActiveCompilation() {
    MOZ_ASSERT(hasActiveCompilation());
    activeCompilations_--;
  }

  template <typename Map>
  Map* acquireMap(JSContext* cx) {
    MOZ_ASSERT(hasActiveCompilation());
    return mapPool_.acquire<Map>(cx);
  }

  template <typename Map>
  void releaseMap(Map** map) {
    MOZ_ASSERT(hasActiveCompilation());
    MOZ_ASSERT(map);
    if (*map) {
      mapPool_.release(map);
    }
  }

  template <typename Vector>
  Vector* acquireVector(JSContext* cx) {
    MOZ_ASSERT(hasActiveCompilation());
    return vectorPool_.acquire<Vector>(cx);
  }

  template <typename Vector>
  void releaseVector(Vector** vec) {
    MOZ_ASSERT(hasActiveCompilation());
    MOZ_ASSERT(vec);
    if (*vec) {
      vectorPool_.release(vec);
    }
  }

  void purge() {
    if (!hasActiveCompilation()) {
      mapPool_.purgeAll();
      vectorPool_.purgeAll();
    }
  }
};

#define POOLED_COLLECTION_PTR_METHODS(N, T)                                   \
  NameCollectionPool& pool_;                                                  \
  T* collection_;                                                             \
                                                                              \
  T& collection() {                                                           \
    MOZ_ASSERT(collection_);                                                  \
    return *collection_;                                                      \
  }                                                                           \
                                                                              \
  const T& collection() const {                                               \
    MOZ_ASSERT(collection_);                                                  \
    return *collection_;                                                      \
  }                                                                           \
                                                                              \
 public:                                                                      \
  explicit N(NameCollectionPool& pool) : pool_(pool), collection_(nullptr) {} \
                                                                              \
  ~N() { pool_.release##T(&collection_); }                                    \
                                                                              \
  bool acquire(JSContext* cx) {                                               \
    MOZ_ASSERT(!collection_);                                                 \
    collection_ = pool_.acquire##T<T>(cx);                                    \
    return !!collection_;                                                     \
  }                                                                           \
                                                                              \
  explicit operator bool() const { return !!collection_; }                    \
                                                                              \
  T* operator->() { return &collection(); }                                   \
                                                                              \
  const T* operator->() const { return &collection(); }                       \
                                                                              \
  T& operator*() { return collection(); }                                     \
                                                                              \
  const T& operator*() const { return collection(); }

template <typename Map>
class PooledMapPtr {
  POOLED_COLLECTION_PTR_METHODS(PooledMapPtr, Map)
};

template <typename Vector>
class PooledVectorPtr {
  POOLED_COLLECTION_PTR_METHODS(PooledVectorPtr, Vector)

  typename Vector::ElementType& operator[](size_t index) {
    return collection()[index];
  }

  const typename Vector::ElementType& operator[](size_t index) const {
    return collection()[index];
  }
};

#undef POOLED_COLLECTION_PTR_METHODS

}  // namespace frontend
}  // namespace js

namespace mozilla {

template <typename T>
struct IsPod<js::frontend::RecyclableAtomMapValueWrapper<T>> : IsPod<T> {};

}  // namespace mozilla

#endif  // frontend_NameCollections_h