blob: 07948c2c3bd6fdb9572905fbdd57bc6387e3b0b2 [file] [log] [blame]
// 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 ComparisonInstr;
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 NonStreamingWriteStream;
class OsrEntryInstr;
class ParsedFunction;
class ParallelMoveInstr;
class PhiInstr;
class Range;
class ReadStream;
class TargetEntryInstr;
class TokenPosition;
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(ComparisonInstr*) \
V(CompileType*) \
V(ConstantInstr*) \
V(Definition*) \
V(double) \
V(Environment*) \
V(const Field&) \
V(const ICData*) \
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 compiler::ffi::NativeCallingConvention&) \
V(const Object&) \
V(ParallelMoveInstr*) \
V(PhiInstr*) \
V(Range*) \
V(Representation) \
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*)
// 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);
// Default implementation of 'Write' method, when
// specialization for a particular type is not provided.
// This struct is used for the partial template instantiations below.
template <typename T>
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 <>
struct WriteTrait<intptr_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 <>
struct WriteTrait<uintptr_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
}
};
template <typename T>
void Write(typename WriteTrait<T>::ArgType x) {
WriteTrait<T>::Write(this, x);
}
#define DECLARE_WRITE_METHOD(type) \
template <> \
void Write<type>(type x);
IL_SERIALIZABLE_TYPE_LIST(DECLARE_WRITE_METHOD)
#undef DECLARE_WRITE_METHOD
template <typename T>
void WriteRef(T x);
#define DECLARE_WRITE_REF_METHOD(type) \
template <> \
void WriteRef<type>(type x);
IL_SERIALIZABLE_REF_TYPE_LIST(DECLARE_WRITE_REF_METHOD)
#undef DECLARE_WRITE_REF_METHOD
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_; }
IsolateGroup* isolate_group() const { return isolate_group_; }
bool can_write_refs() const { return can_write_refs_; }
private:
void WriteObjectImpl(const Object& x, intptr_t cid, intptr_t object_index);
// Used to track scopes of recursive types during serialization.
struct TypeScope {
TypeScope(FlowGraphSerializer* serializer, bool is_recursive)
: serializer_(serializer),
is_recursive_(is_recursive),
was_writing_recursive_type_(serializer->writing_recursive_type_) {
serializer->writing_recursive_type_ = is_recursive;
}
~TypeScope() {
serializer_->writing_recursive_type_ = was_writing_recursive_type_;
}
// Returns true if type of the current scope can be canonicalized
// during deserialization. Recursive types which were not
// fully deserialized should not be canonicalized.
bool CanBeCanonicalized() const {
return !is_recursive_ || !was_writing_recursive_type_;
}
FlowGraphSerializer* const serializer_;
const bool is_recursive_;
const bool was_writing_recursive_type_;
};
NonStreamingWriteStream* stream_;
Zone* zone_;
IsolateGroup* isolate_group_;
Heap* heap_;
intptr_t object_counter_ = 0;
bool can_write_refs_ = false;
bool writing_recursive_type_ = false;
};
// 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();
// Default implementation of 'Read' method, when
// specialization for a particular type is not provided.
// This struct is used for the partial template instantiations below.
template <typename T>
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 <>
struct ReadTrait<intptr_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 <>
struct ReadTrait<uintptr_t> {
static uintptr_t Read(FlowGraphDeserializer* d) {
#ifdef ARCH_IS_64_BIT
return d->Read<uint64_t>();
#else
return d->Read<uint32_t>();
#endif
}
};
template <typename T>
T Read() {
return ReadTrait<T>::Read(this);
}
#define DECLARE_READ_METHOD(type) \
template <> \
type Read<type>();
IL_SERIALIZABLE_TYPE_LIST(DECLARE_READ_METHOD)
#undef DECLARE_READ_METHOD
template <typename T>
T ReadRef();
#define DECLARE_READ_REF_METHOD(type) \
template <> \
type ReadRef<type>();
IL_SERIALIZABLE_REF_TYPE_LIST(DECLARE_READ_REF_METHOD)
#undef DECLARE_READ_REF_METHOD
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 std::move(array);
}
private:
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);
InstancePtr MaybeCanonicalize(const Instance& obj,
intptr_t object_index,
bool can_be_canonicalized);
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;
GrowableArray<intptr_t> pending_canonicalization_;
};
} // namespace dart
#endif // RUNTIME_VM_COMPILER_BACKEND_IL_SERIALIZER_H_