// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Slot is an abstraction that describes an readable (and possibly writeable)
// location within an object.
//
// In general slots follow the memory model for normal Dart fields - but can
// also be used to describe locations that don't have corresponding Field
// object, i.e. fields within native objects like arrays or contexts.
//
// Slot objects created by the compiler have an identity. If two slots F and G
// are different then compiler assumes that store into F can't alias a load
// from G and vice versa.
//
// All slots can be split into 4 categories:
//
//   - slots for fields of native classes (Array, Closure, etc);
//   - slots for type arguments;
//   - slots for captured variable;
//   - slots for normal Dart fields (e.g. those that Field object).
//

#ifndef RUNTIME_VM_COMPILER_BACKEND_SLOT_H_
#define RUNTIME_VM_COMPILER_BACKEND_SLOT_H_

#if defined(DART_PRECOMPILED_RUNTIME)
#error "AOT runtime should not use compiler sources (including header files)"
#endif  // defined(DART_PRECOMPILED_RUNTIME)

#include "vm/compiler/backend/compile_type.h"
#include "vm/compiler/backend/locations.h"
#include "vm/thread.h"

namespace dart {

class LocalScope;
class LocalVariable;
class ParsedFunction;

// The list of slots that correspond to nullable boxed fields of native
// Dart objects in the following format:
//
//     V(class_name, underlying_type, field_name, exact_type, FINAL|VAR|WEAK)
//
// - class_name and field_name specify the name of the host class and the name
//   of the field respectively;
// - underlying_type: the Raw class which holds the field;
// - exact_type specifies exact type of the field (any load from this field
//   would only yield instances of this type);
// - the last component specifies whether field behaves like a final field
//   (i.e. initialized once at construction time and does not change after
//   that), ordinary mutable field or a weak field (can be modified by GC).
#define NULLABLE_TAGGED_NATIVE_DART_SLOTS_LIST(V)                              \
  V(Array, UntaggedArray, type_arguments, TypeArguments, FINAL)                \
  V(Finalizer, UntaggedFinalizer, type_arguments, TypeArguments, FINAL)        \
  V(FinalizerBase, UntaggedFinalizerBase, all_entries, Set, VAR)               \
  V(FinalizerBase, UntaggedFinalizerBase, detachments, Dynamic, VAR)           \
  V(FinalizerBase, UntaggedFinalizer, entries_collected, FinalizerEntry, VAR)  \
  V(FinalizerEntry, UntaggedFinalizerEntry, value, Dynamic, WEAK)              \
  V(FinalizerEntry, UntaggedFinalizerEntry, detach, Dynamic, WEAK)             \
  V(FinalizerEntry, UntaggedFinalizerEntry, token, Dynamic, VAR)               \
  V(FinalizerEntry, UntaggedFinalizerEntry, finalizer, FinalizerBase, WEAK)    \
  V(FinalizerEntry, UntaggedFinalizerEntry, next, FinalizerEntry, VAR)         \
  V(Function, UntaggedFunction, signature, FunctionType, FINAL)                \
  V(Context, UntaggedContext, parent, Context, FINAL)                          \
  V(FunctionType, UntaggedFunctionType, type_parameters, TypeParameters,       \
    FINAL)                                                                     \
  V(ReceivePort, UntaggedReceivePort, send_port, SendPort, FINAL)              \
  V(ReceivePort, UntaggedReceivePort, handler, Closure, VAR)                   \
  V(ImmutableLinkedHashBase, UntaggedLinkedHashBase, index,                    \
    TypedDataUint32Array, VAR_NOSANITIZETHREAD)                                \
  V(Instance, UntaggedInstance, native_fields_array, Dynamic, VAR)             \
  V(SuspendState, UntaggedSuspendState, function_data, Dynamic, VAR)           \
  V(SuspendState, UntaggedSuspendState, then_callback, Closure, VAR)           \
  V(SuspendState, UntaggedSuspendState, error_callback, Closure, VAR)          \
  V(TypeParameters, UntaggedTypeParameters, flags, Array, FINAL)               \
  V(TypeParameters, UntaggedTypeParameters, bounds, TypeArguments, FINAL)      \
  V(TypeParameters, UntaggedTypeParameters, defaults, TypeArguments, FINAL)    \
  V(WeakProperty, UntaggedWeakProperty, key, Dynamic, WEAK)                    \
  V(WeakProperty, UntaggedWeakProperty, value, Dynamic, WEAK)                  \
  V(WeakReference, UntaggedWeakReference, target, Dynamic, WEAK)               \
  V(WeakReference, UntaggedWeakReference, type_arguments, TypeArguments, FINAL)

// The list of slots that correspond to non-nullable boxed fields of native
// Dart objects that contain integers in the following format:
//
//     V(class_name, underlying_type, field_name, exact_type, FINAL|VAR)
//
// - class_name and field_name specify the name of the host class and the name
//   of the field respectively;
// - underlying_type: the Raw class which holds the field;
// - exact_type specifies exact type of the field (any load from this field
//   would only yield instances of this type);
// - the last component specifies whether field behaves like a final field
//   (i.e. initialized once at construction time and does not change after
//   that) or like a non-final field.
#define NONNULLABLE_INT_TAGGED_NATIVE_DART_SLOTS_LIST(V)                       \
  V(Array, UntaggedArray, length, Smi, FINAL)                                  \
  V(Closure, UntaggedClosure, length_and_flags, Smi, FINAL)                    \
  V(Closure, UntaggedClosure, hash, Smi, VAR_NOSANITIZETHREAD)                 \
  V(GrowableObjectArray, UntaggedGrowableObjectArray, length, Smi, VAR)        \
  V(TypedDataBase, UntaggedTypedDataBase, length, Smi, FINAL)                  \
  V(TypedDataView, UntaggedTypedDataView, offset_in_bytes, Smi, FINAL)         \
  V(String, UntaggedString, length, Smi, FINAL)                                \
  V(LinkedHashBase, UntaggedLinkedHashBase, hash_mask, Smi, VAR)               \
  V(LinkedHashBase, UntaggedLinkedHashBase, used_data, Smi, VAR)               \
  V(LinkedHashBase, UntaggedLinkedHashBase, deleted_keys, Smi, VAR)            \
  V(ArgumentsDescriptor, UntaggedArray, type_args_len, Smi, FINAL)             \
  V(ArgumentsDescriptor, UntaggedArray, positional_count, Smi, FINAL)          \
  V(ArgumentsDescriptor, UntaggedArray, count, Smi, FINAL)                     \
  V(ArgumentsDescriptor, UntaggedArray, size, Smi, FINAL)                      \
  V(Record, UntaggedRecord, shape, Smi, FINAL)                                 \
  V(TypeArguments, UntaggedTypeArguments, hash, Smi, VAR)                      \
  V(TypeArguments, UntaggedTypeArguments, length, Smi, FINAL)                  \
  V(AbstractType, UntaggedTypeArguments, hash, Smi, VAR)

// The list of slots that correspond to non-nullable boxed fields of native
// Dart objects that do not contain integers in the following format:
//
//     V(class_name, underlying_type, field_name, exact_type, FINAL|VAR)
//
// - class_name and field_name specify the name of the host class and the name
//   of the field respectively;
// - underlying_type: the Raw class which holds the field;
// - exact_type specifies exact type of the field (any load from this field
//   would only yield instances of this type);
// - the last component specifies whether field behaves like a final field
//   (i.e. initialized once at construction time and does not change after
//   that) or like a non-final field.
#define NONNULLABLE_NONINT_TAGGED_NATIVE_DART_SLOTS_LIST(V)                    \
  V(Closure, UntaggedClosure, function, Function, FINAL)                       \
  V(Finalizer, UntaggedFinalizer, callback, Closure, FINAL)                    \
  V(NativeFinalizer, UntaggedFinalizer, callback, Pointer, FINAL)              \
  V(Function, UntaggedFunction, data, Dynamic, FINAL)                          \
  V(FunctionType, UntaggedFunctionType, named_parameter_names, Array, FINAL)   \
  V(FunctionType, UntaggedFunctionType, parameter_types, Array, FINAL)         \
  V(GrowableObjectArray, UntaggedGrowableObjectArray, data, Array, VAR)        \
  V(TypedDataView, UntaggedTypedDataView, typed_data, Dynamic, FINAL)          \
  V(LinkedHashBase, UntaggedLinkedHashBase, index, TypedDataUint32Array, VAR)  \
  V(LinkedHashBase, UntaggedLinkedHashBase, data, Array, VAR)                  \
  V(ImmutableLinkedHashBase, UntaggedLinkedHashBase, data, ImmutableArray,     \
    FINAL)                                                                     \
  V(TypeParameters, UntaggedTypeParameters, names, Array, FINAL)               \
  V(UnhandledException, UntaggedUnhandledException, exception, Dynamic, FINAL) \
  V(UnhandledException, UntaggedUnhandledException, stacktrace, Dynamic, FINAL)

// List of slots that correspond to fields of native objects that contain
// unboxed values in the following format:
//
//     V(class_name, underlying_type, field_name, representation, FINAL|VAR)
//
// - class_name and field_name specify the name of the host class and the name
//   of the field respectively;
// - underlying_type: the Raw class which holds the field;
// - representation specifies the representation of the bits stored within
//   the unboxed field (minus the kUnboxed prefix);
// - the last component specifies whether field behaves like a final field
//   (i.e. initialized once at construction time and does not change after
//   that) or like a non-final field.
//
// Note: As the underlying field is unboxed, these slots cannot be nullable.
//
// Note: Currently LoadFieldInstr::IsImmutableLengthLoad() assumes that no
// unboxed slots represent length loads.
#define UNBOXED_NATIVE_DART_SLOTS_LIST(V)                                      \
  V(AbstractType, UntaggedAbstractType, flags, Uint32, FINAL)                  \
  V(ClosureData, UntaggedClosureData, packed_fields, Uint32, FINAL)            \
  V(FinalizerEntry, UntaggedFinalizerEntry, external_size, IntPtr, VAR)        \
  V(Function, UntaggedFunction, kind_tag, Uint32, FINAL)                       \
  V(FunctionType, UntaggedFunctionType, packed_parameter_counts, Uint32,       \
    FINAL)                                                                     \
  V(FunctionType, UntaggedFunctionType, packed_type_parameter_counts, Uint16,  \
    FINAL)                                                                     \
  V(SubtypeTestCache, UntaggedSubtypeTestCache, num_inputs, Uint32, FINAL)

// Native slots containing untagged addresses that do not exist in JIT mode.
// See UNTAGGED_NATIVE_DART_SLOTS_LIST for the format.
#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
#define AOT_ONLY_UNTAGGED_NATIVE_DART_SLOTS_LIST(V)                            \
  V(Closure, UntaggedClosure, entry_point, false, FINAL)
#else
#define AOT_ONLY_UNTAGGED_NATIVE_DART_SLOTS_LIST(V)
#endif

// List of slots that correspond to fields of native Dart objects containing
// untagged addresses in the following format:
//
//     V(class_name, underlying_type, field_name, gc_may_move, FINAL|VAR)
//
// - class_name and field_name specify the name of the host class and the name
//   of the field respectively;
// - underlying_type: the Raw class which holds the field;
// - gc_may_move: whether the untagged address contained in this field is a
//   pointer to memory that may be moved by the GC, which means a value loaded
//   from this field is invalidated by any instruction that can cause GC;
// - the last component specifies whether field behaves like a final field
//   (i.e. initialized once at construction time and does not change after
//   that) or like a non-final field.
//
// Note: As the underlying field is untagged, these slots cannot be nullable.
//
// Note: All slots for fields that contain untagged addresses are given
// the kUntagged representation.
#define UNTAGGED_NATIVE_DART_SLOTS_LIST(V)                                     \
  AOT_ONLY_UNTAGGED_NATIVE_DART_SLOTS_LIST(V)                                  \
  V(Function, UntaggedFunction, entry_point, false, FINAL)                     \
  V(FinalizerBase, UntaggedFinalizerBase, isolate, false, VAR)                 \
  V(PointerBase, UntaggedPointerBase, data, true, VAR)

// List of slots that correspond to fields of non-Dart objects containing
// tagged addresses of Dart objects in the following format:
//
//     V(class_name, _, field_name, exact_type, FINAL|VAR)
//
// - class_name and field_name specify the name of the host class and the name
//   of the field respectively;
// - exact_type specifies exact type of the field (any load from this field
//   would only yield instances of this type);
// - the last component specifies whether field behaves like a final field
//   (i.e. initialized once at construction time and does not change after
//   that) or like a non-final field.
//
// Note: Currently LoadFieldInstr::IsImmutableLengthLoad() assumes that no
// slots of non-Dart values represent length loads.
#define NULLABLE_TAGGED_NATIVE_NONDART_SLOTS_LIST(V)                           \
  V(Isolate, _, finalizers, GrowableObjectArray, VAR)                          \
  V(LocalHandle, _, ptr, Dynamic, VAR)                                         \
  V(ObjectStore, _, record_field_names, Array, VAR)                            \
  V(PersistentHandle, _, ptr, Dynamic, VAR)                                    \
  V(Thread, _, current_tag, UserTag, VAR)                                      \
  V(Thread, _, default_tag, UserTag, VAR)                                      \
  V(Thread, _, thread_locals, Array, VAR)

// List of slots that correspond to fields of non-Dart objects containing
// unboxed values in the following format:
//
//     V(class_name, _, field_name, representation, FINAL|VAR)
//
// - class_name and field_name specify the name of the host class and the name
//   of the field respectively;
// - representation specifies the representation of the bits stored within
//   the unboxed field (minus the kUnboxed prefix);
// - the last component specifies whether field behaves like a final field
//   (i.e. initialized once at construction time and does not change after
//   that) or like a non-final field.
//
// Note: As the underlying field is unboxed, these slots cannot be nullable.
//
// Note: Currently LoadFieldInstr::IsImmutableLengthLoad() assumes that no
// slots of non-Dart values represent length loads.
#define UNBOXED_NATIVE_NONDART_SLOTS_LIST(V)                                   \
  V(StreamInfo, _, enabled, IntPtr, VAR)

// List of slots that correspond to fields of non-Dart objects containing
// untagged addresses in the following format:
//
//     V(class_name, _, field_name, gc_may_move, FINAL|VAR)
//
// - class_name and field_name specify the name of the host class and the name
//   of the field respectively;
// - gc_may_move: whether the untagged address contained in this field is a
//   pointer to memory that may be moved by the GC, which means a value loaded
//   from this field is invalidated by any instruction that can cause GC;
// - the last component specifies whether field behaves like a final field
//   (i.e. initialized once at construction time and does not change after
//   that) or like a non-final field.
//
// Note: As the underlying field is untagged, these slots cannot be nullable.
//
// Note: All slots for fields that contain untagged addresses are given
// the kUntagged representation.
//
// Note: while Thread::isolate_ and IsolateGroup::object_store_ aren't const
// fields, they should never change during a given execution of the code
// generated for a function and the compiler only does intra-procedural
// load optimizations.
#define UNTAGGED_NATIVE_NONDART_SLOTS_LIST(V)                                  \
  V(IsolateGroup, _, object_store, false, FINAL)                               \
  V(Thread, _, api_top_scope, false, VAR)                                      \
  V(Thread, _, isolate, false, FINAL)                                          \
  V(Thread, _, isolate_group, false, FINAL)                                    \
  V(Thread, _, dart_stream, false, FINAL)                                      \
  V(Thread, _, service_extension_stream, false, FINAL)

// No untagged slot on a non-Dart object should contain a GC-movable address.
// The gc_may_move field is only there so that any code that operates on
// UNTAGGED_NATIVE_SLOTS_LIST can use that field as desired.
#define CHECK_NATIVE_NONDART_SLOT(__, ___, ____, gc_may_move, _____)           \
  static_assert(!gc_may_move);
UNTAGGED_NATIVE_NONDART_SLOTS_LIST(CHECK_NATIVE_NONDART_SLOT)
#undef CHECK_NATIVE_NONDART_SLOT

// For uses that need any native slot that contain an unboxed integer. Such uses
// can only use the following arguments for each entry:
//     V(class_name, _, field_name, rep, FINAL|VAR)
#define UNBOXED_NATIVE_SLOTS_LIST(V)                                           \
  UNBOXED_NATIVE_DART_SLOTS_LIST(V)                                            \
  UNBOXED_NATIVE_NONDART_SLOTS_LIST(V)

// For uses that need any native slot that contain an untagged address. Such
// uses can only use the following arguments for each entry:
//     V(class_name, _, field_name, gc_may_move, FINAL|VAR)
#define UNTAGGED_NATIVE_SLOTS_LIST(V)                                          \
  UNTAGGED_NATIVE_DART_SLOTS_LIST(V)                                           \
  UNTAGGED_NATIVE_NONDART_SLOTS_LIST(V)

// For uses that need any native slot that does not contain a Dart object. Such
// uses can only use the following arguments for each entry:
//     V(class_name, _, field_name, _, FINAL|VAR)
#define NOT_TAGGED_NATIVE_SLOTS_LIST(V)                                        \
  UNBOXED_NATIVE_SLOTS_LIST(V)                                                 \
  UNTAGGED_NATIVE_SLOTS_LIST(V)

// For uses that need any native slot that is guaranteed to contain a tagged
// integer. Such uses can only use the following arguments for each entry:
//     V(class_name, _, field_name, exact_type, FINAL|VAR)
#define TAGGED_INT_NATIVE_SLOTS_LIST(V)                                        \
  NONNULLABLE_INT_TAGGED_NATIVE_DART_SLOTS_LIST(V)

// For uses that need any native slot that contains a tagged object which is not
// guaranteed to be a integer. This includes nullable integer slots, since
// those slots may return a non-integer value (null). Such uses can
// only use the following arguments for each entry:
//     V(class_name, _, field_name, exact_type, FINAL|VAR|WEAK)
#define TAGGED_NONINT_NATIVE_SLOTS_LIST(V)                                     \
  NULLABLE_TAGGED_NATIVE_DART_SLOTS_LIST(V)                                    \
  NONNULLABLE_NONINT_TAGGED_NATIVE_DART_SLOTS_LIST(V)                          \
  NULLABLE_TAGGED_NATIVE_NONDART_SLOTS_LIST(V)

// For uses that need any native slot that is not guaranteed to contain an
// integer, whether a Dart object or unboxed. Such uses can only use the
// following arguments for each entry:
//     V(class_name, _, field_name, _, FINAL|VAR|WEAK)
#define NOT_INT_NATIVE_SLOTS_LIST(V)                                           \
  TAGGED_NONINT_NATIVE_SLOTS_LIST(V)                                           \
  UNTAGGED_NATIVE_SLOTS_LIST(V)

// For uses that need any native slot on Dart objects that contains a Dart
// object (e.g., for write barrier purposes). Such uses can use the following
// arguments for each entry:
//     V(class_name, underlying_class, field_name, exact_type, FINAL|VAR|WEAK)
#define TAGGED_NATIVE_DART_SLOTS_LIST(V)                                       \
  NULLABLE_TAGGED_NATIVE_DART_SLOTS_LIST(V)                                    \
  NONNULLABLE_INT_TAGGED_NATIVE_DART_SLOTS_LIST(V)                             \
  NONNULLABLE_NONINT_TAGGED_NATIVE_DART_SLOTS_LIST(V)

// For uses that need any native slot that is not on a Dart object or does
// not contain a Dart object (e.g., for write barrier purposes). Such uses
// can only use the following arguments for each entry:
//     V(class_name, _, field_name, _, FINAL|VAR)
#define NOT_TAGGED_NATIVE_DART_SLOTS_LIST(V)                                   \
  NULLABLE_TAGGED_NATIVE_NONDART_SLOTS_LIST(V)                                 \
  NOT_TAGGED_NATIVE_SLOTS_LIST(V)

// For uses that need any native slot that contains a Dart object. Such uses can
// only use the following arguments for each entry:
//     V(class_name, _, field_name, exact_type, FINAL|VAR|WEAK)
#define TAGGED_NATIVE_SLOTS_LIST(V)                                            \
  TAGGED_INT_NATIVE_SLOTS_LIST(V)                                              \
  TAGGED_NONINT_NATIVE_SLOTS_LIST(V)

// For uses that need all native slots. Such uses can only use the following
// arguments for each entry:
//     V(class_name, _, field_name, _, FINAL|VAR|WEAK)
#define NATIVE_SLOTS_LIST(V)                                                   \
  TAGGED_NATIVE_SLOTS_LIST(V)                                                  \
  NOT_TAGGED_NATIVE_SLOTS_LIST(V)

// For tagged slots, the cid should either be Dynamic or the precise cid
// of the values stored in the corresponding field. That means the cid should
// not be the cid of an abstract superclass, because then the code will assume
// the cid of retrieved values is always the given cid.
//
// Note: If we ever need native slots with CompileTypes created from an
// AbstractType instead, then a new base category should be created for those,
// possibly replacing the cid field with the name of the abstract type.
#define CHECK_TAGGED_NATIVE_SLOT(__, ___, ____, field_type, _____)             \
  static_assert(k##field_type##Cid != kObjectCid);                             \
  static_assert(k##field_type##Cid != kInstanceCid);                           \
  static_assert(k##field_type##Cid != kIntegerCid);                            \
  static_assert(k##field_type##Cid != kStringCid);                             \
  static_assert(k##field_type##Cid != kAbstractTypeCid);
TAGGED_NATIVE_SLOTS_LIST(CHECK_TAGGED_NATIVE_SLOT)
#undef CHECK_NULLABLE_TAGGED_NATIVE_SLOT

// Currently we only create slots with CompileTypes created from a precise cid,
// so integer slots listed here must only contain Smis (or Mints, but no slot
// currently does has only Mints, adjust this check if one is added).
//
// Note: If we ever add a category of native slots with AbstractType-based
// CompileTypes that always contain integers, then add additional checks that
// the AbstractTypes of those slots are subtypes of Integer.
#define CHECK_INT_NATIVE_SLOT(__, ___, ____, field_type, _____)                \
  static_assert(k##field_type##Cid == kSmiCid);
TAGGED_INT_NATIVE_SLOTS_LIST(CHECK_INT_NATIVE_SLOT)
#undef CHECK_INT_NATIVE_SLOT

// Any slot with an integer type should go into the correct category.
//
// Note: If we ever add native slots with AbstractType-based CompileTypes, then
// add appropriate checks that the AbstractType is not a subtype of Integer.
#define CHECK_NONINT_NATIVE_SLOT(__, ___, ____, field_type, _____)             \
  static_assert(k##field_type##Cid != kSmiCid);                                \
  static_assert(k##field_type##Cid != kMintCid);
TAGGED_NONINT_NATIVE_SLOTS_LIST(CHECK_NONINT_NATIVE_SLOT)
#undef CHECK_NONINT_NATIVE_SLOT

class FieldGuardState {
 public:
  FieldGuardState() : state_(0) {}
  explicit FieldGuardState(const Field& field);

  intptr_t guarded_cid() const { return GuardedCidBits::decode(state_); }
  bool is_nullable() const { return IsNullableBit::decode(state_); }

 private:
  using GuardedCidBits = BitField<int32_t, ClassIdTagType, 0, 20>;
  using IsNullableBit = BitField<int32_t, bool, GuardedCidBits::kNextBit, 1>;

  const int32_t state_;
};

// Slot is an abstraction that describes an readable (and possibly writeable)
// location within an object.
//
// Slot objects returned by Slot::Get* methods have identity and can be
// compared by pointer. If two slots are different they must not alias.
// If two slots can alias - they must be represented by identical
// slot object.
class Slot : public ZoneObject {
 public:
  // clang-format off
  enum class Kind : uint8_t {
    // Native slots are identified by their kind - each native slot has its own.
#define DECLARE_KIND(ClassName, __, FieldName, ___, ____)                      \
  k##ClassName##_##FieldName,
    NATIVE_SLOTS_LIST(DECLARE_KIND)
#undef DECLARE_KIND

    // A slot used to store type arguments.
    kTypeArguments,

    // A slot at a specific [index] in a [UntaggedTypeArgument] vector.
    kTypeArgumentsIndex,

    // A slot corresponding to an array element at given offset.
    // Only used during allocation sinking and in MaterializeObjectInstr.
    kArrayElement,

    // A slot corresponding to a record field at the given offset.
    kRecordField,

    // A slot corresponding to a Closure element at given offset.
    kClosureElement,

    // A slot within a Context object that contains a value of a captured
    // local variable.
    kCapturedVariable,

    // A slot that corresponds to a Dart field (has corresponding Field object).
    kDartField,
  };
  // clang-format on

  // Returns a slot that represents length field for the given [array_cid].
  static const Slot& GetLengthFieldForArrayCid(intptr_t array_cid);

  // Return a slot that represents type arguments field for the given class.
  //
  // We do not distinguish type argument fields within disjoint
  // class hierarchies: type argument fields at the same offset would be
  // represented by the same Slot object. Type argument slots are final
  // so disambiguating type arguments fields does not improve alias analysis.
  static const Slot& GetTypeArgumentsSlotFor(Thread* thread, const Class& cls);

  // Returns a slot at a specific [index] in a [UntaggedTypeArgument] vector.
  static const Slot& GetTypeArgumentsIndexSlot(Thread* thread, intptr_t index);

  // Returns a slot corresponding to an array element at [offset_in_bytes].
  static const Slot& GetArrayElementSlot(Thread* thread,
                                         intptr_t offset_in_bytes);

  // Returns a slot corresponding to a record field at [offset_in_bytes].
  static const Slot& GetRecordFieldSlot(Thread* thread,
                                        intptr_t offset_in_bytes);

  // Returns a slot corresponding to a Closure element at [offset_in_bytes].
  static const Slot& GetClosureElementSlot(Thread* thread,
                                           intptr_t offset_in_bytes);

  // Returns 'Closure.context' slot for the given closure [function].
  static const Slot& GetClosureContextSlot(Thread* thread,
                                           const Function& function);

  // Returns a slot that represents the given captured local variable.
  static const Slot& GetContextVariableSlotFor(Thread* thread,
                                               const LocalVariable& var);

  // Returns a slot that represents the given Dart field.
  static const Slot& Get(const Field& field,
                         const ParsedFunction* parsed_function);

  // Convenience getters for native slots.
#define DEFINE_GETTER(ClassName, __, FieldName, ___, ____)                     \
  static const Slot& ClassName##_##FieldName() {                               \
    return GetNativeSlot(Kind::k##ClassName##_##FieldName);                    \
  }

  NATIVE_SLOTS_LIST(DEFINE_GETTER)
#undef DEFINE_GETTER

  Kind kind() const { return kind_; }
  bool IsDartField() const { return kind() == Kind::kDartField; }
  bool IsLocalVariable() const { return kind() == Kind::kCapturedVariable; }
  bool IsTypeArguments() const { return kind() == Kind::kTypeArguments; }
  bool IsArgumentOfType() const { return kind() == Kind::kTypeArgumentsIndex; }
  bool IsArrayElement() const { return kind() == Kind::kArrayElement; }
  bool IsRecordField() const { return kind() == Kind::kRecordField; }
  bool IsClosureElement() const { return kind() == Kind::kClosureElement; }
  bool IsLengthSlot() const;
  bool IsImmutableLengthSlot() const;

  const char* Name() const;

  intptr_t offset_in_bytes() const { return offset_in_bytes_; }

  // Currently returns the representation of unboxed native fields and kTagged
  // for most other types of fields. One special case: fields marked as
  // containing non-nullable ints in AOT kernel, which have the kUnboxedInt64
  // representation.
  Representation representation() const { return representation_; }

  bool is_immutable() const { return IsImmutableBit::decode(flags_); }

  bool is_weak() const { return IsWeakBit::decode(flags_); }

  // Returns true if properties of this slot were based on the guarded state
  // of the corresponding Dart field.
  bool is_guarded_field() const { return IsGuardedBit::decode(flags_); }

  bool is_compressed() const { return IsCompressedBit::decode(flags_); }

  // Returns true if the field is an unboxed native field that may contain an
  // inner pointer to a GC-movable object.
  bool may_contain_inner_pointer() const {
    return MayContainInnerPointerBit::decode(flags_);
  }

  bool is_no_sanitize_thread() const {
    return IsNoSanitizeThreadBit::decode(flags_);
  }

  // Type information about values that can be read from this slot.
  CompileType type() const { return type_; }

  const Field& field() const {
    ASSERT(IsDartField());
    ASSERT(data_ != nullptr);
    return *DataAs<const Field>();
  }

  bool Equals(const Slot& other) const;
  uword Hash() const;

  bool IsIdentical(const Slot& other) const { return this == &other; }

  bool IsContextSlot() const {
    return kind() == Kind::kCapturedVariable || kind() == Kind::kContext_parent;
  }

  bool is_tagged() const { return !IsNonTaggedBit::decode(flags_); }
  bool has_untagged_instance() const {
    return HasUntaggedInstanceBit::decode(flags_);
  }

  void Write(FlowGraphSerializer* s) const;
  static const Slot& Read(FlowGraphDeserializer* d);

 private:
  Slot(Kind kind,
       int8_t flags,
       intptr_t offset_in_bytes,
       const void* data,
       CompileType type,
       Representation representation,
       const FieldGuardState& field_guard_state = FieldGuardState())
      : kind_(kind),
        flags_(flags),
        offset_in_bytes_(offset_in_bytes),
        representation_(representation),
        field_guard_state_(field_guard_state),
        data_(data),
        type_(type) {}

  Slot(const Slot& other)
      : Slot(other.kind_,
             other.flags_,
             other.offset_in_bytes_,
             other.data_,
             other.type_,
             other.representation_,
             other.field_guard_state_) {}

  template <typename T>
  const T* DataAs() const {
    return static_cast<const T*>(data_);
  }

  static const Slot& GetCanonicalSlot(
      Thread* thread,
      Kind kind,
      int8_t flags,
      intptr_t offset_in_bytes,
      const void* data,
      CompileType type,
      Representation representation,
      const FieldGuardState& field_guard_state = FieldGuardState());

  static const Slot& GetNativeSlot(Kind kind);

  const FieldGuardState& field_guard_state() const {
    return field_guard_state_;
  }

  const Kind kind_;
  const int8_t flags_;
  const intptr_t offset_in_bytes_;
  const Representation representation_;

  const FieldGuardState field_guard_state_;

  // Kind dependent data:
  //   - name as a Dart String object for local variables;
  //   - name as a C string for native slots;
  //   - Field object for Dart fields;
  const void* data_;

  CompileType type_;

  using IsImmutableBit = BitField<decltype(flags_), bool, 0, 1>;
  using IsWeakBit =
      BitField<decltype(flags_), bool, IsImmutableBit::kNextBit, 1>;
  using IsGuardedBit = BitField<decltype(flags_), bool, IsWeakBit::kNextBit, 1>;
  using IsCompressedBit =
      BitField<decltype(flags_), bool, IsGuardedBit::kNextBit, 1>;
  // Stores whether a field isn't tagged so that tagged is the default value
  using IsNonTaggedBit =
      BitField<decltype(flags_), bool, IsCompressedBit::kNextBit, 1>;
  using MayContainInnerPointerBit =
      BitField<decltype(flags_), bool, IsNonTaggedBit::kNextBit, 1>;
  using HasUntaggedInstanceBit =
      BitField<decltype(flags_), bool, MayContainInnerPointerBit::kNextBit, 1>;
  using IsNoSanitizeThreadBit =
      BitField<decltype(flags_), bool, HasUntaggedInstanceBit::kNextBit, 1>;

  friend class SlotCache;
};

}  // namespace dart

#endif  // RUNTIME_VM_COMPILER_BACKEND_SLOT_H_
