blob: 62c3b6a1f9726167f1c484cd2de27041ba79c47d [file] [log] [blame] [edit]
// 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_