// Copyright (c) 2022, 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.

#ifndef RUNTIME_VM_COMPILER_BACKEND_IL_SERIALIZER_H_
#define RUNTIME_VM_COMPILER_BACKEND_IL_SERIALIZER_H_

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

#include <utility>  // For std::move.

#include "platform/globals.h"
#include "vm/allocation.h"
#include "vm/compiler/backend/locations.h"

namespace dart {

class AliasIdentity;
class BlockEntryInstr;
class CallTargets;
class CatchBlockEntryInstr;
struct CidRangeValue;
class Cids;
class Code;
class ConditionInstr;
class CompileType;
class Definition;
class Environment;
class FunctionEntryInstr;
class Instruction;
class FlowGraph;
class GraphEntryInstr;
class Heap;
class IndirectEntryInstr;
class JoinEntryInstr;
class LocalVariable;
class LocationSummary;
class MoveOperands;
class MoveSchedule;
class NonStreamingWriteStream;
class OsrEntryInstr;
class ParsedFunction;
class ParallelMoveInstr;
class PhiInstr;
class Range;
class ReadStream;
class RecordShape;
class TargetEntryInstr;
class TokenPosition;
class TryEntryInstr;

namespace compiler {
struct TableSelector;

namespace ffi {
class CallbackMarshaller;
class CallMarshaller;
class NativeCallingConvention;
}  // namespace ffi
}  // namespace compiler

// The list of types which are handled by flow graph serializer/deserializer.
// For each type there is a corresponding Write<T>(T) and Read<T>() methods.
//
// This list includes all types of fields of IL instructions
// which are serialized via DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS macro,
// except enum types which are unwrapped with serializable_type_t.
//
// The list is sorted alphabetically by type name.
#define IL_SERIALIZABLE_TYPE_LIST(V)                                           \
  V(AliasIdentity)                                                             \
  V(const AbstractType&)                                                       \
  V(const AbstractType*)                                                       \
  V(const Array&)                                                              \
  V(bool)                                                                      \
  V(const compiler::ffi::CallbackMarshaller&)                                  \
  V(const compiler::ffi::CallMarshaller&)                                      \
  V(const CallTargets&)                                                        \
  V(const char*)                                                               \
  V(CidRangeValue)                                                             \
  V(const Cids&)                                                               \
  V(const Class&)                                                              \
  V(const Code&)                                                               \
  V(ConditionInstr*)                                                           \
  V(CompileType*)                                                              \
  V(ConstantInstr*)                                                            \
  V(Definition*)                                                               \
  V(double)                                                                    \
  V(Environment*)                                                              \
  V(const Field&)                                                              \
  V(const ICData*)                                                             \
  V(const Instance&)                                                           \
  V(int8_t)                                                                    \
  V(int16_t)                                                                   \
  V(int32_t)                                                                   \
  V(int64_t)                                                                   \
  V(const Function&)                                                           \
  V(const FunctionType&)                                                       \
  V(Instruction*)                                                              \
  V(const LocalVariable&)                                                      \
  V(LocationSummary*)                                                          \
  V(MoveOperands*)                                                             \
  V(const MoveSchedule*)                                                       \
  V(const Object&)                                                             \
  V(ParallelMoveInstr*)                                                        \
  V(PhiInstr*)                                                                 \
  V(Range*)                                                                    \
  V(RecordShape)                                                               \
  V(Representation)                                                            \
  V(simd128_value_t)                                                           \
  V(const Slot&)                                                               \
  V(const Slot*)                                                               \
  V(const String&)                                                             \
  V(const compiler::TableSelector*)                                            \
  V(TokenPosition)                                                             \
  V(const TypeArguments&)                                                      \
  V(const TypeParameters&)                                                     \
  V(const TypedData&)                                                          \
  V(uint8_t)                                                                   \
  V(uint16_t)                                                                  \
  V(uint32_t)                                                                  \
  V(uint64_t)                                                                  \
  V(Value*)

// List of types serializable as references.
#define IL_SERIALIZABLE_REF_TYPE_LIST(V)                                       \
  V(BlockEntryInstr*)                                                          \
  V(CatchBlockEntryInstr*)                                                     \
  V(Definition*)                                                               \
  V(FunctionEntryInstr*)                                                       \
  V(IndirectEntryInstr*)                                                       \
  V(JoinEntryInstr*)                                                           \
  V(OsrEntryInstr*)                                                            \
  V(TargetEntryInstr*)                                                         \
  V(TryEntryInstr*)

// Serializes flow graph, including constants and references
// to objects of program structure.
//
// Each IL instruction is serialized in 2 step:
// - the main step (T::WriteTo / T::T()) serializes
//   instruction fields, basically everything required to
//   re-create instruction object.
// - the extra step (T::WriteExtra / T::ReadExtra) serializes
//   references to other instructions, including inputs,
//   environments, locations (may reference constants) and successors.
//
class FlowGraphSerializer : public ValueObject {
 public:
  explicit FlowGraphSerializer(NonStreamingWriteStream* stream);
  ~FlowGraphSerializer();

  // Writes [flow_graph] into the stream.
  // The graph should be compacted via CompactSSA().
  // [detached_defs] should contain all definitions which are
  // detached from the graph but can still be referenced from
  // environments.
  void WriteFlowGraph(const FlowGraph& flow_graph,
                      const ZoneGrowableArray<Definition*>& detached_defs);

  // Implementation of 'Write' method, specialized for a particular type.
  // This struct is used for the partial template instantiations below.
  //
  // Explicit (full) specializations of 'Write' method are not provided as
  // gcc doesn't support explicit template specializations of members of
  // a non-template class
  // (CWG 730 https://cplusplus.github.io/CWG/issues/730.html).
  // The 2nd template argument is used to make all template instantiations
  // partial as gcc doesn't support explicit (full) template specializations
  // in class scope (CWG 727 https://cplusplus.github.io/CWG/issues/727.html).
  template <typename T, class = void>
  struct WriteTrait {
    using ArgType = T;
  };

  template <typename T>
  struct WriteTrait<GrowableArray<T>> {
    using ArgType = const GrowableArray<T>&;
    static void Write(FlowGraphSerializer* s, ArgType x) {
      const intptr_t len = x.length();
      s->Write<intptr_t>(len);
      for (intptr_t i = 0; i < len; ++i) {
        s->Write<T>(x[i]);
      }
    }
  };

  template <typename T>
  struct WriteTrait<const GrowableArray<T>&> {
    using ArgType = const GrowableArray<T>&;
    static void Write(FlowGraphSerializer* s, ArgType x) {
      WriteTrait<GrowableArray<T>>::Write(s, x);
    }
  };

  template <typename T>
  struct WriteTrait<ZoneGrowableArray<T>*> {
    using ArgType = const ZoneGrowableArray<T>*;
    static void Write(FlowGraphSerializer* s, ArgType x) {
      if (x == nullptr) {
        s->Write<intptr_t>(-1);
        return;
      }
      const intptr_t len = x->length();
      s->Write<intptr_t>(len);
      for (intptr_t i = 0; i < len; ++i) {
        s->Write<T>((*x)[i]);
      }
    }
  };

  template <typename T>
  struct WriteTrait<const ZoneGrowableArray<T>&> {
    using ArgType = const ZoneGrowableArray<T>&;
    static void Write(FlowGraphSerializer* s, ArgType x) {
      WriteTrait<ZoneGrowableArray<T>*>::Write(s, &x);
    }
  };

  // Specialization in case intptr_t is not mapped to intN_t.
  template <typename T>
  struct WriteTrait<T,
                    std::enable_if_t<std::is_same_v<intptr_t, T> &&
                                     !std::is_same_v<intptr_t, int32_t> &&
                                     !std::is_same_v<intptr_t, int64_t>>> {
    using ArgType = intptr_t;
    static void Write(FlowGraphSerializer* s, intptr_t x) {
#ifdef ARCH_IS_64_BIT
      s->Write<int64_t>(x);
#else
      s->Write<int32_t>(x);
#endif
    }
  };

  // Specialization in case uintptr_t is not mapped to uintN_t.
  template <typename T>
  struct WriteTrait<T,
                    std::enable_if_t<std::is_same_v<uintptr_t, T> &&
                                     !std::is_same_v<uintptr_t, uint32_t> &&
                                     !std::is_same_v<uintptr_t, uint64_t>>> {
    using ArgType = uintptr_t;
    static void Write(FlowGraphSerializer* s, uintptr_t x) {
#ifdef ARCH_IS_64_BIT
      s->Write<uint64_t>(x);
#else
      s->Write<uint32_t>(x);
#endif
    }
  };

#define DECLARE_WRITE_TRAIT(type)                                              \
  template <typename T>                                                        \
  struct WriteTrait<T, std::enable_if_t<std::is_same_v<type, T>>> {            \
    using ArgType = type;                                                      \
    static void Write(FlowGraphSerializer* s, type x);                         \
  };
  IL_SERIALIZABLE_TYPE_LIST(DECLARE_WRITE_TRAIT)
#undef DECLARE_WRITE_TRAIT

  template <typename T>
  void Write(typename WriteTrait<T>::ArgType x) {
    WriteTrait<T>::Write(this, x);
  }

  // Implementation of 'WriteRef' method, specialized for a particular type.
  // This struct is used for the partial template instantiations below.
  //
  // Explicit (full) specializations of 'WriteRef' method are not provided as
  // gcc doesn't support explicit template specializations of members of
  // a non-template class
  // (CWG 730 https://cplusplus.github.io/CWG/issues/730.html).
  // The 2nd template argument is used to make all template instantiations
  // partial as gcc doesn't support explicit (full) template specializations
  // in class scope (CWG 727 https://cplusplus.github.io/CWG/issues/727.html).
  template <typename T, class = void>
  struct WriteRefTrait {};

#define DECLARE_WRITE_REF_TRAIT(type)                                          \
  template <typename T>                                                        \
  struct WriteRefTrait<T, std::enable_if_t<std::is_same_v<type, T>>> {         \
    static void WriteRef(FlowGraphSerializer* s, T x);                         \
  };
  IL_SERIALIZABLE_REF_TYPE_LIST(DECLARE_WRITE_REF_TRAIT)
#undef DECLARE_WRITE_REF_TRAIT

  template <typename T>
  void WriteRef(T x) {
    WriteRefTrait<T>::WriteRef(this, x);
  }

  template <typename T>
  void WriteGrowableArrayOfRefs(const GrowableArray<T>& array) {
    const intptr_t len = array.length();
    Write<intptr_t>(len);
    for (intptr_t i = 0; i < len; ++i) {
      WriteRef<T>(array[i]);
    }
  }

  BaseWriteStream* stream() const { return stream_; }
  Zone* zone() const { return zone_; }
  Thread* thread() const { return thread_; }
  IsolateGroup* isolate_group() const { return isolate_group_; }
  Heap* heap() const { return heap_; }
  bool can_write_refs() const { return can_write_refs_; }

 private:
  void AddBaseObject(const Object& x);
  void WriteObjectImpl(const Object& x, intptr_t cid, intptr_t object_index);
  bool IsWritten(const Object& obj);
  bool HasEnclosingTypes(const Object& obj);
  bool WriteObjectWithEnclosingTypes(const Object& type);
  void WriteEnclosingTypes(const Object& type,
                           intptr_t num_free_fun_type_params);

  NonStreamingWriteStream* stream_;
  Zone* zone_;
  Thread* thread_;
  IsolateGroup* isolate_group_;
  Heap* heap_;
  intptr_t object_counter_ = 0;
  bool can_write_refs_ = false;
  intptr_t num_free_fun_type_params_ = kMaxInt;
};

// Deserializes flow graph.
// All constants and types are canonicalized during deserialization.
class FlowGraphDeserializer : public ValueObject {
 public:
  FlowGraphDeserializer(const ParsedFunction& parsed_function,
                        ReadStream* stream);

  const ParsedFunction& parsed_function() const { return parsed_function_; }

  Zone* zone() const { return zone_; }
  ReadStream* stream() const { return stream_; }
  Thread* thread() const { return thread_; }
  IsolateGroup* isolate_group() const { return isolate_group_; }

  GraphEntryInstr* graph_entry() const { return graph_entry_; }
  void set_graph_entry(GraphEntryInstr* entry) { graph_entry_ = entry; }

  BlockEntryInstr* current_block() const { return current_block_; }
  void set_current_block(BlockEntryInstr* block) { current_block_ = block; }

  BlockEntryInstr* block(intptr_t block_id) const {
    BlockEntryInstr* b = blocks_[block_id];
    ASSERT(b != nullptr);
    return b;
  }
  void set_block(intptr_t block_id, BlockEntryInstr* block) {
    ASSERT(blocks_[block_id] == nullptr);
    blocks_[block_id] = block;
  }

  Definition* definition(intptr_t ssa_temp_index) const {
    Definition* def = definitions_[ssa_temp_index];
    ASSERT(def != nullptr);
    return def;
  }
  void set_definition(intptr_t ssa_temp_index, Definition* def) {
    ASSERT(definitions_[ssa_temp_index] == nullptr);
    definitions_[ssa_temp_index] = def;
  }

  FlowGraph* ReadFlowGraph();

  // Implementation of 'Read' method, specialized for a particular type.
  // This struct is used for the partial template instantiations below.
  //
  // Explicit (full) specializations of 'Read' method are not provided as
  // gcc doesn't support explicit template specializations of members of
  // a non-template class
  // (CWG 730 https://cplusplus.github.io/CWG/issues/730.html).
  // The 2nd template argument is used to make all template instantiations
  // partial as gcc doesn't support explicit (full) template specializations
  // in class scope (CWG 727 https://cplusplus.github.io/CWG/issues/727.html).
  template <typename T, class = void>
  struct ReadTrait {};

  template <typename T>
  struct ReadTrait<GrowableArray<T>> {
    static GrowableArray<T> Read(FlowGraphDeserializer* d) {
      const intptr_t len = d->Read<intptr_t>();
      GrowableArray<T> array(len);
      for (int i = 0; i < len; ++i) {
        array.Add(d->Read<T>());
      }
      return array;
    }
  };

  template <typename T>
  struct ReadTrait<const GrowableArray<T>&> {
    static const GrowableArray<T>& Read(FlowGraphDeserializer* d) {
      return ReadTrait<GrowableArray<T>>::Read(d);
    }
  };

  template <typename T>
  struct ReadTrait<ZoneGrowableArray<T>*> {
    static ZoneGrowableArray<T>* Read(FlowGraphDeserializer* d) {
      const intptr_t len = d->Read<intptr_t>();
      if (len < 0) {
        return nullptr;
      }
      auto* array = new (d->zone()) ZoneGrowableArray<T>(d->zone(), len);
      for (int i = 0; i < len; ++i) {
        array->Add(d->Read<T>());
      }
      return array;
    }
  };

  template <typename T>
  struct ReadTrait<const ZoneGrowableArray<T>&> {
    static const ZoneGrowableArray<T>& Read(FlowGraphDeserializer* d) {
      return *ReadTrait<ZoneGrowableArray<T>*>::Read(d);
    }
  };

  // Specialization in case intptr_t is not mapped to intN_t.
  template <typename T>
  struct ReadTrait<T,
                   std::enable_if_t<std::is_same_v<intptr_t, T> &&
                                    !std::is_same_v<intptr_t, int32_t> &&
                                    !std::is_same_v<intptr_t, int64_t>>> {
    static intptr_t Read(FlowGraphDeserializer* d) {
#ifdef ARCH_IS_64_BIT
      return d->Read<int64_t>();
#else
      return d->Read<int32_t>();
#endif
    }
  };

  // Specialization in case uintptr_t is not mapped to uintN_t.
  template <typename T>
  struct ReadTrait<T,
                   std::enable_if_t<std::is_same_v<uintptr_t, T> &&
                                    !std::is_same_v<uintptr_t, uint32_t> &&
                                    !std::is_same_v<uintptr_t, uint64_t>>> {
    static uintptr_t Read(FlowGraphDeserializer* d) {
#ifdef ARCH_IS_64_BIT
      return d->Read<uint64_t>();
#else
      return d->Read<uint32_t>();
#endif
    }
  };

#define DECLARE_READ_TRAIT(type)                                               \
  template <typename T>                                                        \
  struct ReadTrait<T, std::enable_if_t<std::is_same_v<type, T>>> {             \
    static type Read(FlowGraphDeserializer* d);                                \
  };
  IL_SERIALIZABLE_TYPE_LIST(DECLARE_READ_TRAIT)
#undef DECLARE_READ_TRAIT

  template <typename T>
  T Read() {
    return ReadTrait<T>::Read(this);
  }

  // Implementation of 'ReadRef' method, specialized for a particular type.
  // This struct is used for the partial template instantiations below.
  //
  // Explicit (full) specializations of 'ReadRef' method are not provided as
  // gcc doesn't support explicit template specializations of members of
  // a non-template class
  // (CWG 730 https://cplusplus.github.io/CWG/issues/730.html).
  // The 2nd template argument is used to make all template instantiations
  // partial as gcc doesn't support explicit (full) template specializations
  // in class scope (CWG 727 https://cplusplus.github.io/CWG/issues/727.html).
  template <typename T, class = void>
  struct ReadRefTrait {};

#define DECLARE_READ_REF_TRAIT(type)                                           \
  template <typename T>                                                        \
  struct ReadRefTrait<T, std::enable_if_t<std::is_same_v<type, T>>> {          \
    static T ReadRef(FlowGraphDeserializer* d);                                \
  };
  IL_SERIALIZABLE_REF_TYPE_LIST(DECLARE_READ_REF_TRAIT)
#undef DECLARE_READ_REF_TRAIT

  template <typename T>
  T ReadRef() {
    return ReadRefTrait<T>::ReadRef(this);
  }

  template <typename T>
  GrowableArray<T> ReadGrowableArrayOfRefs() {
    const intptr_t len = Read<intptr_t>();
    GrowableArray<T> array(len);
    for (int i = 0; i < len; ++i) {
      array.Add(ReadRef<T>());
    }
    return array;
  }

 private:
  void AddBaseObject(const Object& x);
  ClassPtr GetClassById(classid_t id) const;
  const Object& ReadObjectImpl(intptr_t cid, intptr_t object_index);
  void SetObjectAt(intptr_t object_index, const Object& object);
  const Object& ReadObjectWithEnclosingTypes();

  const ParsedFunction& parsed_function_;
  ReadStream* stream_;
  Zone* zone_;
  Thread* thread_;
  IsolateGroup* isolate_group_;

  // Deserialized objects.
  GraphEntryInstr* graph_entry_ = nullptr;
  BlockEntryInstr* current_block_ = nullptr;
  GrowableArray<BlockEntryInstr*> blocks_;
  GrowableArray<Definition*> definitions_;
  GrowableArray<const Object*> objects_;
  intptr_t object_counter_ = 0;
};

}  // namespace dart

#endif  // RUNTIME_VM_COMPILER_BACKEND_IL_SERIALIZER_H_
