// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_OBJECTS_ALLOCATION_SITE_H_
#define V8_OBJECTS_ALLOCATION_SITE_H_

#include "src/objects.h"
#include "src/objects/struct.h"

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

enum InstanceType : uint16_t;

class AllocationSite : public Struct {
 public:
  NEVER_READ_ONLY_SPACE
  static const uint32_t kMaximumArrayBytesToPretransition = 8 * 1024;
  static const double kPretenureRatio;
  static const int kPretenureMinimumCreated = 100;

  // Values for pretenure decision field.
  enum PretenureDecision {
    kUndecided = 0,
    kDontTenure = 1,
    kMaybeTenure = 2,
    kTenure = 3,
    kZombie = 4,
    kLastPretenureDecisionValue = kZombie
  };

  const char* PretenureDecisionName(PretenureDecision decision);

  // Contains either a Smi-encoded bitfield or a boilerplate. If it's a Smi the
  // AllocationSite is for a constructed Array.
  DECL_ACCESSORS(transition_info_or_boilerplate, Object)
  DECL_ACCESSORS(boilerplate, JSObject)
  DECL_INT_ACCESSORS(transition_info)

  // nested_site threads a list of sites that represent nested literals
  // walked in a particular order. So [[1, 2], 1, 2] will have one
  // nested_site, but [[1, 2], 3, [4]] will have a list of two.
  DECL_ACCESSORS(nested_site, Object)

  // Bitfield containing pretenuring information.
  DECL_INT32_ACCESSORS(pretenure_data)

  DECL_INT32_ACCESSORS(pretenure_create_count)
  DECL_ACCESSORS(dependent_code, DependentCode)

  // heap->allocation_site_list() points to the last AllocationSite which form
  // a linked list through the weak_next property. The GC might remove elements
  // from the list by updateing weak_next.
  DECL_ACCESSORS(weak_next, Object)

  inline void Initialize();

  // Checks if the allocation site contain weak_next field;
  inline bool HasWeakNext() const;

  // This method is expensive, it should only be called for reporting.
  bool IsNested();

  // transition_info bitfields, for constructed array transition info.
  class ElementsKindBits : public BitField<ElementsKind, 0, 5> {};
  class DoNotInlineBit : public BitField<bool, 5, 1> {};
  // Unused bits 6-30.

  // Bitfields for pretenure_data
  class MementoFoundCountBits : public BitField<int, 0, 26> {};
  class PretenureDecisionBits : public BitField<PretenureDecision, 26, 3> {};
  class DeoptDependentCodeBit : public BitField<bool, 29, 1> {};
  STATIC_ASSERT(PretenureDecisionBits::kMax >= kLastPretenureDecisionValue);

  // Increments the mementos found counter and returns true when the first
  // memento was found for a given allocation site.
  inline bool IncrementMementoFoundCount(int increment = 1);

  inline void IncrementMementoCreateCount();

  AllocationType GetAllocationType() const;

  void ResetPretenureDecision();

  inline PretenureDecision pretenure_decision() const;
  inline void set_pretenure_decision(PretenureDecision decision);

  inline bool deopt_dependent_code() const;
  inline void set_deopt_dependent_code(bool deopt);

  inline int memento_found_count() const;
  inline void set_memento_found_count(int count);

  inline int memento_create_count() const;
  inline void set_memento_create_count(int count);

  // The pretenuring decision is made during gc, and the zombie state allows
  // us to recognize when an allocation site is just being kept alive because
  // a later traversal of new space may discover AllocationMementos that point
  // to this AllocationSite.
  inline bool IsZombie() const;

  inline bool IsMaybeTenure() const;

  inline void MarkZombie();

  inline bool MakePretenureDecision(PretenureDecision current_decision,
                                    double ratio, bool maximum_size_scavenge);

  inline bool DigestPretenuringFeedback(bool maximum_size_scavenge);

  inline ElementsKind GetElementsKind() const;
  inline void SetElementsKind(ElementsKind kind);

  inline bool CanInlineCall() const;
  inline void SetDoNotInlineCall();

  inline bool PointsToLiteral() const;

  template <AllocationSiteUpdateMode update_or_check =
                AllocationSiteUpdateMode::kUpdate>
  static bool DigestTransitionFeedback(Handle<AllocationSite> site,
                                       ElementsKind to_kind);

  DECL_PRINTER(AllocationSite)
  DECL_VERIFIER(AllocationSite)

  DECL_CAST(AllocationSite)
  static inline bool ShouldTrack(ElementsKind boilerplate_elements_kind);
  static bool ShouldTrack(ElementsKind from, ElementsKind to);
  static inline bool CanTrack(InstanceType type);

  // Layout description.
  // AllocationSite has to start with TransitionInfoOrboilerPlateOffset
  // and end with WeakNext field.
  #define ALLOCATION_SITE_FIELDS(V)                     \
    V(kStartOffset, 0)                                  \
    V(kTransitionInfoOrBoilerplateOffset, kTaggedSize)  \
    V(kNestedSiteOffset, kTaggedSize)                   \
    V(kDependentCodeOffset, kTaggedSize)                \
    V(kCommonPointerFieldEndOffset, 0)                  \
    V(kPretenureDataOffset, kInt32Size)                 \
    V(kPretenureCreateCountOffset, kInt32Size)          \
    /* Size of AllocationSite without WeakNext field */ \
    V(kSizeWithoutWeakNext, 0)                          \
    V(kWeakNextOffset, kTaggedSize)                     \
    /* Size of AllocationSite with WeakNext field */    \
    V(kSizeWithWeakNext, 0)

  DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, ALLOCATION_SITE_FIELDS)
  #undef ALLOCATION_SITE_FIELDS

  class BodyDescriptor;

 private:
  inline bool PretenuringDecisionMade() const;

  OBJECT_CONSTRUCTORS(AllocationSite, Struct);
};

class AllocationMemento : public Struct {
 public:
  // Layout description.
  DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
                                TORQUE_GENERATED_ALLOCATION_MEMENTO_FIELDS)

  DECL_ACCESSORS(allocation_site, Object)

  inline bool IsValid() const;
  inline AllocationSite GetAllocationSite() const;
  inline Address GetAllocationSiteUnchecked() const;

  DECL_PRINTER(AllocationMemento)
  DECL_VERIFIER(AllocationMemento)

  DECL_CAST(AllocationMemento)

  OBJECT_CONSTRUCTORS(AllocationMemento, Struct);
};

}  // namespace internal
}  // namespace v8

#include "src/objects/object-macros-undef.h"

#endif  // V8_OBJECTS_ALLOCATION_SITE_H_