text: /*
     +----------------------------------------------------------------------+
     | HipHop for PHP                                                       |
     +----------------------------------------------------------------------+
     | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com)  |
     +----------------------------------------------------------------------+
     | This source file is subject to version 3.01 of the PHP license,      |
     | that is bundled with this package in the file LICENSE, and is        |
     | available through the world-wide-web at the following url:           |
     | http://www.php.net/license/3_01.txt                                  |
     | If you did not receive a copy of the PHP license and are unable to   |
     | obtain it through the world-wide-web, please send a note to          |
     | license@php.net so we can mail you a copy immediately.               |
     +----------------------------------------------------------------------+
  */
  
  #ifndef incl_HPHP_HEADER_KIND_H_
  #define incl_HPHP_HEADER_KIND_H_
  
  #include "hphp/util/compilation-flags.h"
  
  #include <array>
  #include <atomic>
  #include <cstddef>
  #include <cstdint>
  #include <type_traits>
  
  namespace HPHP {
  
  #define COLLECTION_TYPES              \
    COL(Vector)                         \
    COL(Map)                            \
    COL(Set)                            \
    COL(Pair)                           \
    COL(ImmVector)                      \
    COL(ImmMap)                         \
    COL(ImmSet)
  
  /*
   * every request-allocated object has a 1-byte kind field in its
   * header, from this enum. If you update the enum, be sure to
   * update header_names[] as well, and either unset or change
   * HHVM_REPO_SCHEMA, because kind values are used in HHBC.
   */
  enum class HeaderKind : uint8_t {
    // ArrayKind aliases
    Packed, Mixed, Empty, Apc, Globals,
    // Hack arrays
    Dict, VecArray, Keyset,
    // Other ordinary refcounted heap objects
    String, Resource, Ref,
  
    // Valid kinds for an ObjectData; all but Object and NativeObject are
    // isCppBuiltin()
    Object, NativeObject, WaitHandle, AsyncFuncWH, AwaitAllWH, Closure,
    // Collections. Vector and ImmSet are used for range checks; be careful
    // when adding new collection kinds.
    Vector, Map, Set, Pair, ImmVector, ImmMap, ImmSet,
  
    // other kinds, not used for countable objects.
    AsyncFuncFrame, // NativeNode followed by Frame, Resumable, AFWH
    NativeData, // a NativeData header preceding an HNI ObjectData
    ClosureHdr, // a ClosureHdr preceding a Closure ObjectData
    Cpp, // a managed object with associated C++ type
    SmallMalloc, // small req::malloc'd block
    BigMalloc, // big req::malloc'd block
    BigObj, // big size-tracked object (valid header follows MallocNode)
    Free, // small block in a FreeList
    Hole, // wasted space not in any freelist
    Slab, // header for a contiguous "slab" of small objects
  };
  const unsigned NumHeaderKinds = unsigned(HeaderKind::Slab) + 1;
  extern const std::array<char*,NumHeaderKinds> header_names;
  
  inline bool haveCount(HeaderKind k) {
    return uint8_t(k) < uint8_t(HeaderKind::AsyncFuncFrame);
  }
  
  /*
   * RefCount type for m_count field in refcounted objects.
   *
   * The sign bit flags a reference count as persistent. If a reference count is
   * persistent, it means we should never increment or decrement it: the object
   * lives across requests and may be accessed by multiple threads. Persistent
   * objects can be static or uncounted; static objects have process lifetime,
   * while uncounted objects are freed using the treadmill. Using 8-bit values
   * generates shorter cmp instructions while still being far enough from 0 to be
   * safe.
   *
   * One-bit reference counting:
   *
   * When one_bit_refcount == true, HHVM's reference counting primitives behave
   * very differently. Reference counts are conceptually one bit, where 0 means
   * "one reference" and 1 means "many references". In practice, m_count is a
   * full byte wide, both to accommodate UncountedValue and StaticValue, and so
   * we can operate on m_count directly without any bit twiddling.
   *
   * DecRef becomes `if (m_count == 0) release();`, while IncRef becomes `m_count
   * = 1` (depending on the value of unconditional_one_bit_incref; see
   * runtime/base/countable.h). This means that any object that is ever IncRefed
   * will stick at m_count == 1 until the GC collects it or the end of the
   * request. This also causes spurious COW operations on ararys, and means
   * Object destructors aren't supported.
   *
   * None of this matters to the vast majority of runtime and JIT code. The
   * interpreter and HHIR programs should still use balanced IncRef and DecRef
   * operations as usual, regardless of the build mode. All differences in how
   * reference counts are manipulated are isolated to member functions of
   * Countable/MaybeCountable and the HHIR -> vasm lowering code for the IncRef
   * and DecRef HHIR instructions.
   */
  enum RefCount : std::conditional<one_bit_refcount, int8_t, int32_t>::type {
    OneReference   = one_bit_refcount ? 0 : 1,
    // MultiReference should never be used outside of one-bit mode, so set it to
    // something above RefCountMaxRealistic to trip asserts.
    MultiReference = one_bit_refcount ? 1 : 0x40000000,
  
    // In one_bit_refcount builds, uncountedIncRef will count upwards
    // from UncountedValue to -1, so it needs to be above StaticValue;
    // in regular builds, its going to count downwards towards
    // INT_MIN, so needs to be below StaticValue.
    UncountedValue = one_bit_refcount ? -127 : -128,
    StaticValue    = one_bit_refcount ? -128 : -127,
  
    RefCountMaxRealistic = one_bit_refcount ? MultiReference : (1 << 30) - 1,
  };
  
  using UnsignedRefCount = std::make_unsigned<RefCount>::type;
  
  enum class GCBits : uint8_t {};
  
  /*
   * Header Layout
   *
   * Refcounted types have a 32-bit RefCount normally, or 8-bit plus 24 bits of
   * padding with ONE_BIT_REFCOUNT.
   *
   * 0       32     40      48        50  56
   * [ cnt | kind | marks | DVArray | X  | sizeClass ] Packed, VecArray
   * [ cnt | kind | marks | DVArray | X  |           ] Mixed, Dict
   * [ cnt | kind | marks |                          ] Empty, Apc, Globals, Keyset
   * [ cnt | kind | marks | sizeClass:16             ] String
   * [ cnt | kind | marks | heapSize:16              ] Resource (ResourceHdr)
   * [ cnt | kind | marks |                          ] Ref (RefData)
   * [ cnt | kind | marks | Attribute    |           ] Object..ImmSet (ObjectData)
   *
   * Note: X in bit 50 is the kHasApcTv flag; see array-data.h
   *
   * Note: when an ObjectData is preceded by a special header (AsyncFuncFrame,
   * NativeData, or ClosureHeader), only the special header is marked using
   * the m_marks field; the m_marks field on the interior ObjectData is unused.
   *
   * Special headers have non-refcount uses for m_aux32:
   *
   * 0          32     40      48
   * [ ar_off | kind | marks |              ] AsyncFuncFrame (NativeNode)
   * [ ar_off | kind | marks | tyindex:16   ] NativeData (NativeNode)
   * [ size   | kind | marks |              ] ClosureHeader (ClosureHdr)
   * [        | kind | marks | tyindex:16   ] Cpp, SmallMalloc (MallocNode)
   * [ index  | kind | marks | tyindex:16   ] BigMalloc (MallocNode)
   * [ index  | kind | marks | kIndexUnkown ] BigObj (MallocNode)
   * [ size   | kind | marks |              ] Free, Hole (FreeNode)
   * [        | kind |       |              ] Slab
   */
  
  /*
   * Common header for all heap-allocated objects. Layout is carefully
   * designed to fit in one 64-bit word.
   */
  struct HeapObject {
  protected:
    union {
      struct {
        mutable RefCount m_count;
  #ifdef ONE_BIT_REFCOUNT
        int8_t m_padding[3];
  #endif
        HeaderKind m_kind;
        mutable GCBits m_marks;
        mutable uint16_t m_aux16;
      };
      mutable std::atomic<std::underlying_type<RefCount>::type> m_atomic_count;
      struct {
        uint32_t m_aux32; // usable if the subclass is not refcounted
        uint32_t m_hi32;
      };
      uint64_t m_all64;
    };
  
    // rename aux16
    template<class T> T& aux16() const {
      static_assert(sizeof(T) == 2, "");
      return reinterpret_cast<T&>(m_aux16);
    }
  
    template<class T> T& aux32() {
      static_assert(sizeof(T) == 4, "");
      return reinterpret_cast<T&>(m_aux32);
    }
  
  public:
    // All of the initHeader functions are carefully crafted to properly
    // zero-extend their arguments and compose them together into one 8-byte
    // store. You must carefully audit each one if you change anything about the
    // layout of HeapObject.
  
    static_assert(std::is_unsigned<std::underlying_type<HeaderKind>::type>::value,
                  "initHeader() functions assume HeaderKind is unsigned");
  
    void initHeader(HeaderKind kind, RefCount count) {
      m_all64 = uint64_t(kind) << (8 * offsetof(HeapObject, m_kind)) |
                UnsignedRefCount(count);
    }
  
    void initHeader_32(HeaderKind kind, uint32_t aux32) {
      m_all64 = uint64_t(kind) << (8 * offsetof(HeapObject, m_kind)) |
                aux32;
    }
  
    void initHeader_16(HeaderKind kind, RefCount count, uint16_t aux16) {
      m_all64 = uint64_t(kind)  << (8 * offsetof(HeapObject, m_kind)) |
                uint64_t(aux16) << (8 * offsetof(HeapObject, m_aux16)) |
                UnsignedRefCount(count);
    }
  
    void initHeader_32_16(HeaderKind kind, uint32_t aux32, uint16_t aux16) {
      m_all64 = uint64_t(kind)  << (8 * offsetof(HeapObject, m_kind)) |
                uint64_t(aux16) << (8 * offsetof(HeapObject, m_aux16)) |
                aux32;
    }
  
    void initHeader(const HeapObject& h, RefCount count) {
      m_all64 = uint64_t(h.m_hi32) << (8 * offsetof(HeapObject, m_hi32)) |
                UnsignedRefCount(count);
    }
  
    static constexpr size_t kind_offset() {
      return offsetof(HeapObject, m_kind);
    }
    static constexpr size_t count_offset() {
      return offsetof(HeapObject, m_count);
    }
    static constexpr size_t aux_offset() {
      return offsetof(HeapObject, m_aux16);
    }
  
  public:
    HeaderKind kind() const { return m_kind; }
    GCBits marks() const { return m_marks; }
    void setmarks(GCBits m) const { m_marks = m; }
  };
  static_assert(sizeof(HeapObject) == sizeof(uint64_t),
                "HeapObject is expected to be 8 bytes.");
  
  constexpr auto HeaderKindOffset = HeapObject::kind_offset();
  constexpr auto HeaderAuxOffset = HeapObject::aux_offset();
  
  inline bool isObjectKind(HeaderKind k) {
    return k >= HeaderKind::Object && k <= HeaderKind::ImmSet;
  }
  
  inline bool isArrayKind(HeaderKind k) {
    return k >= HeaderKind::Packed && k <= HeaderKind::Keyset;
  }
  
  inline bool isFreeKind(HeaderKind k) {
    return k >= HeaderKind::Free;
  }
  
  namespace detail {
  // update these if you add more kinds for ObjectData that should
  // pass the isCppBuiltin() predicate.
  constexpr auto FirstCppBuiltin = HeaderKind::WaitHandle;
  constexpr auto LastCppBuiltin = HeaderKind::ImmSet;
  static_assert(uint8_t(LastCppBuiltin) - uint8_t(FirstCppBuiltin) == 10,
                "keep predicate in sync with enum");
  }
  
  // legacy CppBuiltins have custom C++ types (not plain ObjectData layouts)
  // are not considered HNI objects, and do not have NativeData headers.
  inline bool isCppBuiltin(HeaderKind k) {
    return k >= detail::FirstCppBuiltin && k <= detail::LastCppBuiltin;
  }
  
  inline bool hasInstanceDtor(HeaderKind k) {
    static_assert(uint8_t(HeaderKind::NativeObject) + 1 ==
                  uint8_t(detail::FirstCppBuiltin), "");
    return k >= HeaderKind::NativeObject && k <= detail::LastCppBuiltin;
  }
  
  inline bool isHackArrayKind(HeaderKind k) {
    return
      k == HeaderKind::Dict     ||
      k == HeaderKind::VecArray ||
      k == HeaderKind::Keyset;
  }
  
  inline bool isWaithandleKind(HeaderKind k) {
    return k >= HeaderKind::WaitHandle && k <= HeaderKind::AwaitAllWH;
    static_assert((int)HeaderKind::AwaitAllWH - (int)HeaderKind::WaitHandle == 2,
                  "isWaithandleKind requires updating");
  }
  
  inline bool isBigKind(HeaderKind k) {
    return k == HeaderKind::BigObj || k == HeaderKind::BigMalloc;
  }
  
  enum class CollectionType : uint8_t {
  #define COL(name) name = uint8_t(HeaderKind::name),
    COLLECTION_TYPES
  #undef COL
  };
  
  inline bool isVectorCollection(CollectionType ctype) {
    return ctype == CollectionType::Vector || ctype == CollectionType::ImmVector;
  }
  inline bool isMapCollection(CollectionType ctype) {
    return ctype == CollectionType::Map || ctype == CollectionType::ImmMap;
  }
  inline bool isSetCollection(CollectionType ctype) {
    return ctype == CollectionType::Set || ctype == CollectionType::ImmSet;
  }
  inline bool isValidCollection(CollectionType ctype) {
    return uint8_t(ctype) >= uint8_t(CollectionType::Vector) &&
           uint8_t(ctype) <= uint8_t(CollectionType::ImmSet);
  }
  inline bool isMutableCollection(CollectionType ctype) {
    return ctype == CollectionType::Vector ||
           ctype == CollectionType::Map ||
           ctype == CollectionType::Set;
  }
  inline bool isImmutableCollection(CollectionType ctype) {
    return !isMutableCollection(ctype);
  }
  
  inline bool collectionAllowsIntStringKeys(CollectionType ctype) {
    return isSetCollection(ctype) || isMapCollection(ctype);
  }
  
  }
  
  #endif
  