| // 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(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_ |