Files
sdk/runtime/vm/compiler/backend/il_serializer.cc
T
Alexander Markov e893d11b3f [vm,compiler] Fix serialization of ranges and arguments descriptors
Convert range boundaries with symbolic references to other instructions
into constant boundaries to avoid serializing extra references.

Specially handle serialization of arguments descriptors which are
cached in the VM isolate.

TEST=ci

Change-Id: Ifab3bb4d9b037aaefd81d38dd93d47cd4b42cf1d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405571
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
2025-01-27 06:46:05 -08:00

2707 lines
90 KiB
C++

// 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.
#include "vm/compiler/backend/il_serializer.h"
#include "vm/class_id.h"
#include "vm/closure_functions_cache.h"
#if defined(DART_PRECOMPILER)
#include "vm/compiler/aot/precompiler.h"
#endif
#include "vm/compiler/backend/flow_graph.h"
#include "vm/compiler/backend/il.h"
#include "vm/compiler/backend/range_analysis.h"
#include "vm/compiler/frontend/flow_graph_builder.h"
#include "vm/object_store.h"
#include "vm/parser.h"
#define Z zone_
// This file declares write/read methods for each type,
// sorted alphabetically by type/class name (case-insensitive).
// Each "write" method is followed by corresponding "read" method
// or constructor.
namespace dart {
FlowGraphSerializer::FlowGraphSerializer(NonStreamingWriteStream* stream)
: stream_(stream),
zone_(Thread::Current()->zone()),
thread_(Thread::Current()),
isolate_group_(IsolateGroup::Current()),
heap_(IsolateGroup::Current()->heap()) {
// We want to preserve the identity of these, even though they are not const.
AddBaseObject(Object::uninitialized_index());
AddBaseObject(Object::uninitialized_data());
}
FlowGraphSerializer::~FlowGraphSerializer() {
heap_->ResetObjectIdTable();
}
FlowGraphDeserializer::FlowGraphDeserializer(
const ParsedFunction& parsed_function,
ReadStream* stream)
: parsed_function_(parsed_function),
stream_(stream),
zone_(Thread::Current()->zone()),
thread_(Thread::Current()),
isolate_group_(IsolateGroup::Current()) {
// We want to preserve the identity of these, even though they are not const.
AddBaseObject(Object::uninitialized_index());
AddBaseObject(Object::uninitialized_data());
}
ClassPtr FlowGraphDeserializer::GetClassById(classid_t id) const {
return isolate_group()->class_table()->At(id);
}
template <>
void FlowGraphSerializer::WriteTrait<const AbstractType*>::Write(
FlowGraphSerializer* s,
const AbstractType* x) {
if (x == nullptr) {
s->Write<bool>(false);
} else {
s->Write<bool>(true);
s->Write<const AbstractType&>(*x);
}
}
template <>
const AbstractType* FlowGraphDeserializer::ReadTrait<const AbstractType*>::Read(
FlowGraphDeserializer* d) {
if (!d->Read<bool>()) {
return nullptr;
}
return &(d->Read<const AbstractType&>());
}
template <>
void FlowGraphSerializer::WriteTrait<AliasIdentity>::Write(
FlowGraphSerializer* s,
AliasIdentity x) {
x.Write(s);
}
template <>
AliasIdentity FlowGraphDeserializer::ReadTrait<AliasIdentity>::Read(
FlowGraphDeserializer* d) {
return AliasIdentity(d);
}
void AliasIdentity::Write(FlowGraphSerializer* s) const {
s->Write<intptr_t>(value_);
}
AliasIdentity::AliasIdentity(FlowGraphDeserializer* d)
: value_(d->Read<intptr_t>()) {}
void ArgumentsDescriptor::Write(FlowGraphSerializer* s) const {
if (IsCached()) {
// Simple argument descriptors are cached in the VM isolate.
// Write them as arguments count and query cache during deserialization.
ASSERT(TypeArgsLen() == 0);
ASSERT(NamedCount() == 0);
ASSERT(Count() == Size());
ASSERT(array_.InVMIsolateHeap());
s->Write<intptr_t>(Count());
} else {
ASSERT(array_.IsCanonical());
ASSERT(!array_.InVMIsolateHeap());
s->Write<intptr_t>(-1);
s->Write<const Array&>(array_);
}
}
ArrayPtr ArgumentsDescriptor::Read(FlowGraphDeserializer* d) {
const intptr_t num_args = d->Read<intptr_t>();
if (num_args < 0) {
return d->Read<const Array&>().ptr();
}
return NewBoxed(0, num_args);
}
void BlockEntryInstr::WriteTo(FlowGraphSerializer* s) {
TemplateInstruction::WriteTo(s);
s->Write<intptr_t>(block_id_);
s->Write<intptr_t>(try_index_);
s->Write<intptr_t>(stack_depth_);
s->Write<ParallelMoveInstr*>(parallel_move_);
}
BlockEntryInstr::BlockEntryInstr(FlowGraphDeserializer* d)
: TemplateInstruction(d),
block_id_(d->Read<intptr_t>()),
try_index_(d->Read<intptr_t>()),
stack_depth_(d->Read<intptr_t>()),
dominated_blocks_(1),
parallel_move_(d->Read<ParallelMoveInstr*>()) {
d->set_block(block_id_, this);
d->set_current_block(this);
}
void BlockEntryInstr::WriteExtra(FlowGraphSerializer* s) {
TemplateInstruction::WriteExtra(s);
s->WriteRef<BlockEntryInstr*>(dominator_);
s->WriteGrowableArrayOfRefs<BlockEntryInstr*>(dominated_blocks_);
if (parallel_move_ != nullptr) {
parallel_move_->WriteExtra(s);
}
}
void BlockEntryInstr::ReadExtra(FlowGraphDeserializer* d) {
TemplateInstruction::ReadExtra(d);
dominator_ = d->ReadRef<BlockEntryInstr*>();
dominated_blocks_ = d->ReadGrowableArrayOfRefs<BlockEntryInstr*>();
if (parallel_move_ != nullptr) {
parallel_move_->ReadExtra(d);
}
}
template <>
void FlowGraphSerializer::WriteRefTrait<BlockEntryInstr*>::WriteRef(
FlowGraphSerializer* s,
BlockEntryInstr* x) {
ASSERT(s->can_write_refs());
if (x == nullptr) {
s->Write<intptr_t>(-1);
return;
}
const intptr_t id = x->block_id();
ASSERT(id >= 0);
s->Write<intptr_t>(id);
}
template <>
BlockEntryInstr* FlowGraphDeserializer::ReadRefTrait<BlockEntryInstr*>::ReadRef(
FlowGraphDeserializer* d) {
const intptr_t id = d->Read<intptr_t>();
if (id < 0) {
return nullptr;
}
return d->block(id);
}
#define INSTRUCTION_REFS_SERIALIZABLE_AS_BLOCK_ENTRY(V) \
V(CatchBlockEntry, CatchBlockEntryInstr) \
V(FunctionEntry, FunctionEntryInstr) \
V(IndirectEntry, IndirectEntryInstr) \
V(JoinEntry, JoinEntryInstr) \
V(OsrEntry, OsrEntryInstr) \
V(TargetEntry, TargetEntryInstr) \
V(TryEntry, TryEntryInstr)
#define SERIALIZABLE_AS_BLOCK_ENTRY(name, type) \
template <> \
void FlowGraphSerializer::WriteRefTrait<type*>::WriteRef( \
FlowGraphSerializer* s, type* x) { \
s->WriteRef<BlockEntryInstr*>(x); \
} \
template <> \
type* FlowGraphDeserializer::ReadRefTrait<type*>::ReadRef( \
FlowGraphDeserializer* d) { \
BlockEntryInstr* instr = d->ReadRef<BlockEntryInstr*>(); \
ASSERT((instr == nullptr) || instr->Is##name()); \
return static_cast<type*>(instr); \
}
INSTRUCTION_REFS_SERIALIZABLE_AS_BLOCK_ENTRY(SERIALIZABLE_AS_BLOCK_ENTRY)
#undef SERIALIZABLE_AS_BLOCK_ENTRY
#undef INSTRUCTION_REFS_SERIALIZABLE_AS_BLOCK_ENTRY
void BlockEntryWithInitialDefs::WriteTo(FlowGraphSerializer* s) {
BlockEntryInstr::WriteTo(s);
s->Write<GrowableArray<Definition*>>(initial_definitions_);
}
BlockEntryWithInitialDefs::BlockEntryWithInitialDefs(FlowGraphDeserializer* d)
: BlockEntryInstr(d),
initial_definitions_(d->Read<GrowableArray<Definition*>>()) {
for (Definition* def : initial_definitions_) {
def->set_previous(this);
if (auto par = def->AsParameter()) {
par->set_block(this);
}
}
}
void BlockEntryWithInitialDefs::WriteExtra(FlowGraphSerializer* s) {
BlockEntryInstr::WriteExtra(s);
for (Definition* def : initial_definitions_) {
def->WriteExtra(s);
}
}
void BlockEntryWithInitialDefs::ReadExtra(FlowGraphDeserializer* d) {
BlockEntryInstr::ReadExtra(d);
for (Definition* def : initial_definitions_) {
def->ReadExtra(d);
}
}
template <>
void FlowGraphSerializer::WriteTrait<bool>::Write(FlowGraphSerializer* s,
bool x) {
s->stream()->Write<uint8_t>(x ? 1 : 0);
}
template <>
bool FlowGraphDeserializer::ReadTrait<bool>::Read(FlowGraphDeserializer* d) {
return (d->stream()->Read<uint8_t>() != 0);
}
void BranchInstr::WriteExtra(FlowGraphSerializer* s) {
// Branch reuses inputs from its embedded Condition.
// Instruction::WriteExtra is not called to avoid
// writing/reading inputs twice.
WriteExtraWithoutInputs(s);
condition_->WriteExtra(s);
s->WriteRef<TargetEntryInstr*>(true_successor_);
s->WriteRef<TargetEntryInstr*>(false_successor_);
s->WriteRef<TargetEntryInstr*>(constant_target_);
}
void BranchInstr::ReadExtra(FlowGraphDeserializer* d) {
ReadExtraWithoutInputs(d);
condition_->ReadExtra(d);
for (intptr_t i = condition_->InputCount() - 1; i >= 0; --i) {
condition_->InputAt(i)->set_instruction(this);
}
true_successor_ = d->ReadRef<TargetEntryInstr*>();
false_successor_ = d->ReadRef<TargetEntryInstr*>();
constant_target_ = d->ReadRef<TargetEntryInstr*>();
}
template <>
void FlowGraphSerializer::WriteTrait<const compiler::ffi::CallbackMarshaller&>::
Write(FlowGraphSerializer* s, const compiler::ffi::CallbackMarshaller& x) {
s->Write<const Function&>(x.dart_signature());
}
template <>
const compiler::ffi::CallbackMarshaller& FlowGraphDeserializer::ReadTrait<
const compiler::ffi::CallbackMarshaller&>::Read(FlowGraphDeserializer* d) {
const Function& dart_signature = d->Read<const Function&>();
const char* error = nullptr;
return *compiler::ffi::CallbackMarshaller::FromFunction(
d->zone(), dart_signature, &error);
}
template <>
void FlowGraphSerializer::WriteTrait<const compiler::ffi::CallMarshaller&>::
Write(FlowGraphSerializer* s, const compiler::ffi::CallMarshaller& x) {
s->Write<const Function&>(x.dart_signature());
s->Write<int8_t>(x.dart_signature_params_start_at());
s->Write<const FunctionType&>(x.c_signature());
}
template <>
const compiler::ffi::CallMarshaller&
FlowGraphDeserializer::ReadTrait<const compiler::ffi::CallMarshaller&>::Read(
FlowGraphDeserializer* d) {
const Function& dart_signature = d->Read<const Function&>();
const intptr_t dart_signature_params_start_at = d->Read<int8_t>();
const FunctionType& c_signature = d->Read<const FunctionType&>();
const char* error = nullptr;
return *compiler::ffi::CallMarshaller::FromFunction(
d->zone(), dart_signature, dart_signature_params_start_at, c_signature,
&error);
}
template <>
void FlowGraphSerializer::WriteTrait<const CallTargets&>::Write(
FlowGraphSerializer* s,
const CallTargets& x) {
x.Write(s);
}
template <>
const CallTargets& FlowGraphDeserializer::ReadTrait<const CallTargets&>::Read(
FlowGraphDeserializer* d) {
return *(new (d->zone()) CallTargets(d));
}
void CallTargets::Write(FlowGraphSerializer* s) const {
const intptr_t len = cid_ranges_.length();
s->Write<intptr_t>(len);
for (intptr_t i = 0; i < len; ++i) {
TargetInfo* t = TargetAt(i);
s->Write<intptr_t>(t->cid_start);
s->Write<intptr_t>(t->cid_end);
s->Write<const Function&>(*(t->target));
s->Write<intptr_t>(t->count);
s->Write<int8_t>(t->exactness.Encode());
}
}
CallTargets::CallTargets(FlowGraphDeserializer* d) : Cids(d->zone()) {
const intptr_t len = d->Read<intptr_t>();
cid_ranges_.EnsureLength(len, nullptr);
for (intptr_t i = 0; i < len; ++i) {
const intptr_t cid_start = d->Read<intptr_t>();
const intptr_t cid_end = d->Read<intptr_t>();
const Function& target = d->Read<const Function&>();
const intptr_t count = d->Read<intptr_t>();
const StaticTypeExactnessState exactness =
StaticTypeExactnessState::Decode(d->Read<int8_t>());
TargetInfo* t = new (d->zone())
TargetInfo(cid_start, cid_end, &target, count, exactness);
cid_ranges_[i] = t;
}
}
void TryEntryInstr::WriteExtra(FlowGraphSerializer* s) {
JoinEntryInstr::WriteExtra(s);
s->WriteRef<JoinEntryInstr*>(try_body_);
s->WriteRef<CatchBlockEntryInstr*>(catch_target_);
}
void TryEntryInstr::ReadExtra(FlowGraphDeserializer* d) {
JoinEntryInstr::ReadExtra(d);
try_body_ = d->ReadRef<JoinEntryInstr*>();
catch_target_ = d->ReadRef<CatchBlockEntryInstr*>();
}
void CatchBlockEntryInstr::WriteTo(FlowGraphSerializer* s) {
BlockEntryWithInitialDefs::WriteTo(s);
s->Write<const Array&>(catch_handler_types_);
s->Write<intptr_t>(catch_try_index_);
s->Write<bool>(needs_stacktrace_);
s->Write<bool>(is_generated_);
}
CatchBlockEntryInstr::CatchBlockEntryInstr(FlowGraphDeserializer* d)
: BlockEntryWithInitialDefs(d),
predecessor_(nullptr),
catch_handler_types_(d->Read<const Array&>()),
catch_try_index_(d->Read<intptr_t>()),
exception_var_(nullptr),
stacktrace_var_(nullptr),
raw_exception_var_(nullptr),
raw_stacktrace_var_(nullptr),
needs_stacktrace_(d->Read<bool>()),
is_generated_(d->Read<bool>()) {}
template <>
void FlowGraphSerializer::WriteTrait<const char*>::Write(FlowGraphSerializer* s,
const char* x) {
ASSERT(x != nullptr);
const intptr_t len = strlen(x);
s->Write<intptr_t>(len);
s->stream()->WriteBytes(x, len);
}
template <>
const char* FlowGraphDeserializer::ReadTrait<const char*>::Read(
FlowGraphDeserializer* d) {
const intptr_t len = d->Read<intptr_t>();
char* str = d->zone()->Alloc<char>(len + 1);
d->stream()->ReadBytes(str, len);
str[len] = 0;
return str;
}
void CheckConditionInstr::WriteExtra(FlowGraphSerializer* s) {
// CheckCondition reuses inputs from its embedded Condition.
// Instruction::WriteExtra is not called to avoid
// writing/reading inputs twice.
WriteExtraWithoutInputs(s);
condition_->WriteExtra(s);
}
void CheckConditionInstr::ReadExtra(FlowGraphDeserializer* d) {
ReadExtraWithoutInputs(d);
condition_->ReadExtra(d);
for (intptr_t i = condition_->InputCount() - 1; i >= 0; --i) {
condition_->InputAt(i)->set_instruction(this);
}
}
template <>
void FlowGraphSerializer::WriteTrait<CidRangeValue>::Write(
FlowGraphSerializer* s,
CidRangeValue x) {
s->Write<intptr_t>(x.cid_start);
s->Write<intptr_t>(x.cid_end);
}
template <>
CidRangeValue FlowGraphDeserializer::ReadTrait<CidRangeValue>::Read(
FlowGraphDeserializer* d) {
const intptr_t cid_start = d->Read<intptr_t>();
const intptr_t cid_end = d->Read<intptr_t>();
return CidRangeValue(cid_start, cid_end);
}
template <>
void FlowGraphSerializer::WriteTrait<const Cids&>::Write(FlowGraphSerializer* s,
const Cids& x) {
const intptr_t len = x.length();
s->Write<intptr_t>(len);
for (intptr_t i = 0; i < len; ++i) {
const CidRange* r = x.At(i);
s->Write<intptr_t>(r->cid_start);
s->Write<intptr_t>(r->cid_end);
}
}
template <>
const Cids& FlowGraphDeserializer::ReadTrait<const Cids&>::Read(
FlowGraphDeserializer* d) {
Zone* zone = d->zone();
Cids* cids = new (zone) Cids(zone);
const intptr_t len = d->Read<intptr_t>();
for (intptr_t i = 0; i < len; ++i) {
const intptr_t cid_start = d->Read<intptr_t>();
const intptr_t cid_end = d->Read<intptr_t>();
CidRange* r = new (zone) CidRange(cid_start, cid_end);
cids->Add(r);
}
return *cids;
}
template <>
void FlowGraphSerializer::WriteTrait<const Class&>::Write(
FlowGraphSerializer* s,
const Class& x) {
if (x.IsNull()) {
s->Write<classid_t>(kIllegalCid);
return;
}
s->Write<classid_t>(x.id());
}
template <>
const Class& FlowGraphDeserializer::ReadTrait<const Class&>::Read(
FlowGraphDeserializer* d) {
const classid_t cid = d->Read<classid_t>();
if (cid == kIllegalCid) {
return Class::ZoneHandle(d->zone());
}
return Class::ZoneHandle(d->zone(), d->GetClassById(cid));
}
void ConstraintInstr::WriteExtra(FlowGraphSerializer* s) {
TemplateDefinition::WriteExtra(s);
s->WriteRef<TargetEntryInstr*>(target_);
}
void ConstraintInstr::ReadExtra(FlowGraphDeserializer* d) {
TemplateDefinition::ReadExtra(d);
target_ = d->ReadRef<TargetEntryInstr*>();
}
template <>
void FlowGraphSerializer::WriteTrait<const Code&>::Write(FlowGraphSerializer* s,
const Code& x) {
ASSERT(!x.IsNull());
ASSERT(x.IsStubCode());
for (intptr_t i = 0, n = StubCode::NumEntries(); i < n; ++i) {
if (StubCode::EntryAt(i).ptr() == x.ptr()) {
s->Write<intptr_t>(i);
return;
}
}
intptr_t index = StubCode::NumEntries();
ObjectStore* object_store = s->isolate_group()->object_store();
#define MATCH(member, name) \
if (object_store->member() == x.ptr()) { \
s->Write<intptr_t>(index); \
return; \
} \
++index;
OBJECT_STORE_STUB_CODE_LIST(MATCH)
#undef MATCH
UNIMPLEMENTED();
}
template <>
const Code& FlowGraphDeserializer::ReadTrait<const Code&>::Read(
FlowGraphDeserializer* d) {
const intptr_t stub_id = d->Read<intptr_t>();
if (stub_id < StubCode::NumEntries()) {
return StubCode::EntryAt(stub_id);
}
intptr_t index = StubCode::NumEntries();
ObjectStore* object_store = d->isolate_group()->object_store();
#define MATCH(member, name) \
if (index == stub_id) { \
return Code::ZoneHandle(d->zone(), object_store->member()); \
} \
++index;
OBJECT_STORE_STUB_CODE_LIST(MATCH)
#undef MATCH
UNIMPLEMENTED();
}
template <>
void FlowGraphSerializer::WriteTrait<CompileType*>::Write(
FlowGraphSerializer* s,
CompileType* x) {
if (x == nullptr) {
s->Write<bool>(false);
} else {
s->Write<bool>(true);
x->Write(s);
}
}
template <>
CompileType* FlowGraphDeserializer::ReadTrait<CompileType*>::Read(
FlowGraphDeserializer* d) {
if (!d->Read<bool>()) {
return nullptr;
}
return new (d->zone()) CompileType(d);
}
void CompileType::Write(FlowGraphSerializer* s) const {
s->Write<bool>(can_be_null_);
s->Write<bool>(can_be_sentinel_);
s->Write<classid_t>(cid_);
if (type_ == nullptr) {
s->Write<bool>(false);
} else {
s->Write<bool>(true);
s->Write<const AbstractType&>(*type_);
}
}
CompileType::CompileType(FlowGraphDeserializer* d)
: can_be_null_(d->Read<bool>()),
can_be_sentinel_(d->Read<bool>()),
cid_(d->Read<classid_t>()),
type_(nullptr) {
if (d->Read<bool>()) {
type_ = &d->Read<const AbstractType&>();
}
}
void Definition::WriteTo(FlowGraphSerializer* s) {
Instruction::WriteTo(s);
s->Write<Range*>(range_);
s->Write<intptr_t>(temp_index_);
s->Write<intptr_t>(ssa_temp_index_);
s->Write<CompileType*>(type_);
}
Definition::Definition(FlowGraphDeserializer* d)
: Instruction(d),
range_(d->Read<Range*>()),
temp_index_(d->Read<intptr_t>()),
ssa_temp_index_(d->Read<intptr_t>()),
type_(d->Read<CompileType*>()) {
if (HasSSATemp()) {
d->set_definition(ssa_temp_index(), this);
}
if (type_ != nullptr) {
type_->set_owner(this);
}
}
template <>
void FlowGraphSerializer::WriteRefTrait<Definition*>::WriteRef(
FlowGraphSerializer* s,
Definition* x) {
if (!x->HasSSATemp()) {
if (auto* move_arg = x->AsMoveArgument()) {
// Environments of the calls can reference MoveArgument instructions
// and they don't have SSA temps.
// Write a reference to the original definition.
// When reading it is restored using RepairArgumentUsesInEnvironment.
x = move_arg->value()->definition();
} else {
UNREACHABLE();
}
}
ASSERT(x->HasSSATemp());
ASSERT(s->can_write_refs());
s->Write<intptr_t>(x->ssa_temp_index());
}
template <>
Definition* FlowGraphDeserializer::ReadRefTrait<Definition*>::ReadRef(
FlowGraphDeserializer* d) {
return d->definition(d->Read<intptr_t>());
}
template <>
void FlowGraphSerializer::WriteTrait<double>::Write(FlowGraphSerializer* s,
double x) {
s->stream()->Write<int64_t>(bit_cast<int64_t>(x));
}
template <>
double FlowGraphDeserializer::ReadTrait<double>::Read(
FlowGraphDeserializer* d) {
return bit_cast<double>(d->stream()->Read<int64_t>());
}
template <>
void FlowGraphSerializer::WriteTrait<Environment*>::Write(
FlowGraphSerializer* s,
Environment* x) {
ASSERT(s->can_write_refs());
if (x == nullptr) {
s->Write<bool>(false);
} else {
s->Write<bool>(true);
x->Write(s);
}
}
template <>
Environment* FlowGraphDeserializer::ReadTrait<Environment*>::Read(
FlowGraphDeserializer* d) {
if (!d->Read<bool>()) {
return nullptr;
}
return new (d->zone()) Environment(d);
}
void Environment::Write(FlowGraphSerializer* s) const {
s->Write<GrowableArray<Value*>>(values_);
s->Write<intptr_t>(fixed_parameter_count_);
s->Write<uintptr_t>(bitfield_);
s->Write<const Function&>(function_);
s->Write<Environment*>(outer_);
if (locations_ == nullptr) {
s->Write<bool>(false);
} else {
s->Write<bool>(true);
for (intptr_t i = 0, n = values_.length(); i < n; ++i) {
locations_[i].Write(s);
}
}
}
Environment::Environment(FlowGraphDeserializer* d)
: values_(d->Read<GrowableArray<Value*>>()),
locations_(nullptr),
fixed_parameter_count_(d->Read<intptr_t>()),
bitfield_(d->Read<uintptr_t>()),
function_(d->Read<const Function&>()),
outer_(d->Read<Environment*>()) {
for (intptr_t i = 0, n = values_.length(); i < n; ++i) {
Value* value = values_[i];
value->definition()->AddEnvUse(value);
}
if (d->Read<bool>()) {
locations_ = d->zone()->Alloc<Location>(values_.length());
for (intptr_t i = 0, n = values_.length(); i < n; ++i) {
locations_[i] = Location::Read(d);
}
}
}
void FlowGraphSerializer::WriteFlowGraph(
const FlowGraph& flow_graph,
const ZoneGrowableArray<Definition*>& detached_defs) {
ASSERT(!flow_graph.is_licm_allowed());
Write<intptr_t>(flow_graph.current_ssa_temp_index());
Write<intptr_t>(flow_graph.max_block_id());
Write<const Array&>(flow_graph.coverage_array());
PrologueInfo prologue_info = flow_graph.prologue_info();
Write<intptr_t>(prologue_info.min_block_id);
Write<intptr_t>(prologue_info.max_block_id);
// Write instructions
for (auto block : flow_graph.reverse_postorder()) {
Write<Instruction*>(block);
for (auto current : block->instructions()) {
Write<Instruction*>(current);
}
}
Write<Instruction*>(nullptr);
Write<const ZoneGrowableArray<Definition*>&>(detached_defs);
can_write_refs_ = true;
// Write instructions extra info.
// It may contain references to other instructions.
for (auto block : flow_graph.reverse_postorder()) {
block->WriteExtra(this);
for (auto current : block->instructions()) {
current->WriteExtra(this);
}
}
for (auto* instr : detached_defs) {
instr->WriteExtra(this);
}
const auto& optimized_block_order = flow_graph.optimized_block_order();
Write<intptr_t>(optimized_block_order.length());
for (intptr_t i = 0, n = optimized_block_order.length(); i < n; ++i) {
WriteRef<BlockEntryInstr*>(optimized_block_order[i]);
}
Write<intptr_t>(flow_graph.inlining_id());
const InliningInfo& inlining_info = flow_graph.inlining_info();
Write<intptr_t>(inlining_info.inline_id_to_function.length());
ASSERT(inlining_info.inline_id_to_function.length() ==
inlining_info.caller_inline_id.length());
ASSERT(inlining_info.inline_id_to_function.length() ==
inlining_info.inline_id_to_token_pos.length() + 1);
for (intptr_t i = 1, n = inlining_info.inline_id_to_function.length(); i < n;
++i) {
Write<const Function&>(*(inlining_info.inline_id_to_function[i]));
Write<intptr_t>(inlining_info.caller_inline_id[i]);
Write<TokenPosition>(inlining_info.inline_id_to_token_pos[i - 1]);
}
}
FlowGraph* FlowGraphDeserializer::ReadFlowGraph() {
const intptr_t current_ssa_temp_index = Read<intptr_t>();
const intptr_t max_block_id = Read<intptr_t>();
const Array& coverage_array = Read<const Array&>();
const PrologueInfo prologue_info(Read<intptr_t>(), Read<intptr_t>());
definitions_.EnsureLength(current_ssa_temp_index, nullptr);
blocks_.EnsureLength(max_block_id + 1, nullptr);
// Read/create instructions.
ZoneGrowableArray<Instruction*> instructions(16);
Instruction* prev = nullptr;
while (Instruction* instr = Read<Instruction*>()) {
instructions.Add(instr);
if (!instr->IsBlockEntry()) {
ASSERT(prev != nullptr);
prev->LinkTo(instr);
}
prev = instr;
}
ASSERT(graph_entry_ != nullptr);
const auto& detached_defs = Read<const ZoneGrowableArray<Definition*>&>();
// Read instructions extra info.
// It may contain references to other instructions.
for (Instruction* instr : instructions) {
instr->ReadExtra(this);
}
for (auto* instr : detached_defs) {
instr->ReadExtra(this);
}
FlowGraph* flow_graph =
new (Z) FlowGraph(parsed_function(), graph_entry_, max_block_id,
prologue_info, FlowGraph::CompilationMode::kOptimized);
flow_graph->set_current_ssa_temp_index(current_ssa_temp_index);
flow_graph->CreateCommonConstants();
flow_graph->disallow_licm();
flow_graph->set_coverage_array(coverage_array);
{
const intptr_t num_blocks = Read<intptr_t>();
if (num_blocks != 0) {
auto* codegen_block_order = flow_graph->CodegenBlockOrder();
ASSERT(codegen_block_order == &flow_graph->optimized_block_order());
for (intptr_t i = 0; i < num_blocks; ++i) {
codegen_block_order->Add(ReadRef<BlockEntryInstr*>());
}
}
}
flow_graph->set_inlining_id(Read<intptr_t>());
auto& inlining_info = flow_graph->inlining_info();
const intptr_t inlining_info_len = Read<intptr_t>();
ASSERT(inlining_info.inline_id_to_function.length() == 1);
ASSERT(inlining_info.caller_inline_id.length() == 1);
ASSERT(inlining_info.inline_id_to_token_pos.length() == 0);
for (intptr_t i = 1; i < inlining_info_len; ++i) {
inlining_info.inline_id_to_function.Add(&Read<const Function&>());
inlining_info.caller_inline_id.Add(Read<intptr_t>());
inlining_info.inline_id_to_token_pos.Add(Read<TokenPosition>());
}
return flow_graph;
}
template <>
void FlowGraphSerializer::WriteTrait<const Function&>::Write(
FlowGraphSerializer* s,
const Function& x) {
if (x.IsNull()) {
s->Write<int8_t>(-1);
return;
}
Zone* zone = s->zone();
s->Write<int8_t>(x.kind());
switch (x.kind()) {
case UntaggedFunction::kRegularFunction:
case UntaggedFunction::kGetterFunction:
case UntaggedFunction::kSetterFunction:
case UntaggedFunction::kImplicitGetter:
case UntaggedFunction::kImplicitSetter:
case UntaggedFunction::kImplicitStaticGetter:
case UntaggedFunction::kConstructor: {
const auto& owner = Class::Handle(zone, x.Owner());
s->Write<classid_t>(owner.id());
const intptr_t function_index = owner.FindFunctionIndex(x);
ASSERT(function_index >= 0);
s->Write<intptr_t>(function_index);
return;
}
case UntaggedFunction::kImplicitClosureFunction: {
const auto& parent = Function::Handle(zone, x.parent_function());
s->Write<const Function&>(parent);
return;
}
case UntaggedFunction::kFieldInitializer: {
const auto& field = Field::Handle(zone, x.accessor_field());
s->Write<const Field&>(field);
return;
}
case UntaggedFunction::kClosureFunction:
// TODO(alexmarkov): we cannot rely on ClosureFunctionsCache
// as it is lazily populated when compiling functions.
// We need to serialize kernel offset and re-create
// closure functions when reading as needed.
s->Write<intptr_t>(ClosureFunctionsCache::FindClosureIndex(x));
return;
case UntaggedFunction::kMethodExtractor: {
Function& function = Function::Handle(zone, x.extracted_method_closure());
ASSERT(function.IsImplicitClosureFunction());
function = function.parent_function();
s->Write<const Function&>(function);
s->Write<const String&>(String::Handle(zone, x.name()));
return;
}
case UntaggedFunction::kInvokeFieldDispatcher: {
s->Write<const Class&>(Class::Handle(zone, x.Owner()));
s->Write<const String&>(String::Handle(zone, x.name()));
ArgumentsDescriptor(Array::Handle(zone, x.saved_args_desc())).Write(s);
return;
}
case UntaggedFunction::kDynamicInvocationForwarder: {
const auto& target = Function::Handle(zone, x.ForwardingTarget());
s->Write<const Function&>(target);
return;
}
case UntaggedFunction::kFfiTrampoline: {
s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiCallbackKind()));
s->Write<const FunctionType&>(
FunctionType::Handle(zone, x.FfiCSignature()));
s->Write<const Function&>(Function::Handle(zone, x.FfiCallbackTarget()));
s->Write<const Instance&>(
Instance::Handle(zone, x.FfiCallbackExceptionalReturn()));
return;
}
default:
break;
}
switch (x.kind()) {
#define UNIMPLEMENTED_FUNCTION_KIND(kind) \
case UntaggedFunction::k##kind: \
FATAL("Unimplemented WriteTrait<const Function&>::Write for " #kind);
FOR_EACH_RAW_FUNCTION_KIND(UNIMPLEMENTED_FUNCTION_KIND)
#undef UNIMPLEMENTED_FUNCTION_KIND
}
UNREACHABLE();
}
template <>
const Function& FlowGraphDeserializer::ReadTrait<const Function&>::Read(
FlowGraphDeserializer* d) {
const int8_t raw_kind = d->Read<int8_t>();
if (raw_kind < 0) {
return Object::null_function();
}
Zone* zone = d->zone();
const auto kind = static_cast<UntaggedFunction::Kind>(raw_kind);
switch (kind) {
case UntaggedFunction::kRegularFunction:
case UntaggedFunction::kGetterFunction:
case UntaggedFunction::kSetterFunction:
case UntaggedFunction::kImplicitGetter:
case UntaggedFunction::kImplicitSetter:
case UntaggedFunction::kImplicitStaticGetter:
case UntaggedFunction::kConstructor: {
const classid_t owner_class_id = d->Read<classid_t>();
const intptr_t function_index = d->Read<intptr_t>();
const auto& owner = Class::Handle(zone, d->GetClassById(owner_class_id));
const auto& result =
Function::ZoneHandle(zone, owner.FunctionFromIndex(function_index));
ASSERT(!result.IsNull());
return result;
}
case UntaggedFunction::kImplicitClosureFunction: {
const auto& parent = d->Read<const Function&>();
return Function::ZoneHandle(zone, parent.ImplicitClosureFunction());
}
case UntaggedFunction::kFieldInitializer: {
const auto& field = d->Read<const Field&>();
return Function::ZoneHandle(zone, field.EnsureInitializerFunction());
}
case UntaggedFunction::kClosureFunction: {
const intptr_t index = d->Read<intptr_t>();
return Function::ZoneHandle(
zone, ClosureFunctionsCache::ClosureFunctionFromIndex(index));
}
case UntaggedFunction::kMethodExtractor: {
const Function& function = d->Read<const Function&>();
const String& name = d->Read<const String&>();
return Function::ZoneHandle(zone, function.GetMethodExtractor(name));
}
case UntaggedFunction::kInvokeFieldDispatcher: {
const Class& owner = d->Read<const Class&>();
const String& target_name = d->Read<const String&>();
const Array& args_desc =
Array::Handle(zone, ArgumentsDescriptor::Read(d));
return Function::ZoneHandle(
zone,
owner.GetInvocationDispatcher(
target_name, args_desc, UntaggedFunction::kInvokeFieldDispatcher,
/*create_if_absent=*/true));
}
case UntaggedFunction::kDynamicInvocationForwarder: {
const auto& target = d->Read<const Function&>();
auto& name = String::Handle(zone, target.name());
name = Function::CreateDynamicInvocationForwarderName(name);
return Function::ZoneHandle(zone,
target.GetDynamicInvocationForwarder(name));
}
case UntaggedFunction::kFfiTrampoline: {
const FfiCallbackKind kind =
static_cast<FfiCallbackKind>(d->Read<uint8_t>());
const FunctionType& c_signature = d->Read<const FunctionType&>();
const Function& callback_target = d->Read<const Function&>();
const Instance& exceptional_return = d->Read<const Instance&>();
return Function::ZoneHandle(
zone, compiler::ffi::NativeCallbackFunction(
c_signature, callback_target, exceptional_return, kind));
}
default:
UNIMPLEMENTED();
return Object::null_function();
}
}
void FunctionEntryInstr::WriteTo(FlowGraphSerializer* s) {
BlockEntryWithInitialDefs::WriteTo(s);
}
FunctionEntryInstr::FunctionEntryInstr(FlowGraphDeserializer* d)
: BlockEntryWithInitialDefs(d), graph_entry_(d->graph_entry()) {}
void GraphEntryInstr::WriteTo(FlowGraphSerializer* s) {
BlockEntryWithInitialDefs::WriteTo(s);
s->Write<intptr_t>(osr_id_);
s->Write<intptr_t>(entry_count_);
s->Write<intptr_t>(spill_slot_count_);
s->Write<intptr_t>(fixed_slot_count_);
s->Write<bool>(needs_frame_);
}
GraphEntryInstr::GraphEntryInstr(FlowGraphDeserializer* d)
: BlockEntryWithInitialDefs(d),
parsed_function_(d->parsed_function()),
osr_id_(d->Read<intptr_t>()),
entry_count_(d->Read<intptr_t>()),
spill_slot_count_(d->Read<intptr_t>()),
fixed_slot_count_(d->Read<intptr_t>()),
needs_frame_(d->Read<bool>()) {
d->set_graph_entry(this);
}
void GraphEntryInstr::WriteExtra(FlowGraphSerializer* s) {
BlockEntryWithInitialDefs::WriteExtra(s);
s->WriteRef<FunctionEntryInstr*>(normal_entry_);
s->WriteRef<FunctionEntryInstr*>(unchecked_entry_);
s->WriteRef<OsrEntryInstr*>(osr_entry_);
s->WriteGrowableArrayOfRefs<IndirectEntryInstr*>(indirect_entries_);
}
void GraphEntryInstr::ReadExtra(FlowGraphDeserializer* d) {
BlockEntryWithInitialDefs::ReadExtra(d);
normal_entry_ = d->ReadRef<FunctionEntryInstr*>();
unchecked_entry_ = d->ReadRef<FunctionEntryInstr*>();
osr_entry_ = d->ReadRef<OsrEntryInstr*>();
indirect_entries_ = d->ReadGrowableArrayOfRefs<IndirectEntryInstr*>();
}
void GotoInstr::WriteExtra(FlowGraphSerializer* s) {
TemplateInstruction::WriteExtra(s);
if (parallel_move_ != nullptr) {
parallel_move_->WriteExtra(s);
}
s->WriteRef<JoinEntryInstr*>(successor_);
}
void GotoInstr::ReadExtra(FlowGraphDeserializer* d) {
TemplateInstruction::ReadExtra(d);
if (parallel_move_ != nullptr) {
parallel_move_->ReadExtra(d);
}
successor_ = d->ReadRef<JoinEntryInstr*>();
}
template <>
void FlowGraphSerializer::WriteTrait<const ICData*>::Write(
FlowGraphSerializer* s,
const ICData* x) {
if (x == nullptr) {
s->Write<bool>(false);
} else {
s->Write<bool>(true);
ASSERT(!x->IsNull());
s->Write<const Object&>(*x);
}
}
template <>
const ICData* FlowGraphDeserializer::ReadTrait<const ICData*>::Read(
FlowGraphDeserializer* d) {
if (!d->Read<bool>()) {
return nullptr;
}
return &ICData::Cast(d->Read<const Object&>());
}
void IfThenElseInstr::WriteExtra(FlowGraphSerializer* s) {
// IfThenElse reuses inputs from its embedded Condition.
// Definition::WriteExtra is not called to avoid
// writing/reading inputs twice.
WriteExtraWithoutInputs(s);
condition_->WriteExtra(s);
}
void IfThenElseInstr::ReadExtra(FlowGraphDeserializer* d) {
ReadExtraWithoutInputs(d);
condition_->ReadExtra(d);
for (intptr_t i = condition_->InputCount() - 1; i >= 0; --i) {
condition_->InputAt(i)->set_instruction(this);
}
}
void IndirectGotoInstr::WriteTo(FlowGraphSerializer* s) {
TemplateInstruction::WriteTo(s);
s->Write<intptr_t>(offsets_.Length());
}
IndirectGotoInstr::IndirectGotoInstr(FlowGraphDeserializer* d)
: TemplateInstruction(d),
offsets_(TypedData::ZoneHandle(d->zone(),
TypedData::New(kTypedDataInt32ArrayCid,
d->Read<intptr_t>(),
Heap::kOld))) {}
void IndirectGotoInstr::WriteExtra(FlowGraphSerializer* s) {
TemplateInstruction::WriteExtra(s);
s->WriteGrowableArrayOfRefs<TargetEntryInstr*>(successors_);
}
void IndirectGotoInstr::ReadExtra(FlowGraphDeserializer* d) {
TemplateInstruction::ReadExtra(d);
successors_ = d->ReadGrowableArrayOfRefs<TargetEntryInstr*>();
}
template <>
void FlowGraphSerializer::WriteTrait<Instruction*>::Write(
FlowGraphSerializer* s,
Instruction* x) {
if (x == nullptr) {
s->Write<uint8_t>(Instruction::kNumInstructions);
} else {
s->Write<uint8_t>(static_cast<uint8_t>(x->tag()));
x->WriteTo(s);
}
}
template <>
Instruction* FlowGraphDeserializer::ReadTrait<Instruction*>::Read(
FlowGraphDeserializer* d) {
const uint8_t tag = d->Read<uint8_t>();
switch (tag) {
#define READ_INSTRUCTION(type, attrs) \
case Instruction::k##type: \
return new (d->zone()) type##Instr(d);
FOR_EACH_CONCRETE_INSTRUCTION(READ_INSTRUCTION)
#undef READ_INSTRUCTION
case Instruction::kNumInstructions:
return nullptr;
}
UNREACHABLE();
return nullptr;
}
void Instruction::WriteTo(FlowGraphSerializer* s) {
s->Write<intptr_t>(deopt_id_);
s->Write<intptr_t>(inlining_id_);
}
Instruction::Instruction(FlowGraphDeserializer* d)
: deopt_id_(d->Read<intptr_t>()), inlining_id_(d->Read<intptr_t>()) {}
void Instruction::WriteExtra(FlowGraphSerializer* s) {
for (intptr_t i = 0, n = InputCount(); i < n; ++i) {
s->Write<Value*>(InputAt(i));
}
WriteExtraWithoutInputs(s);
}
void Instruction::ReadExtra(FlowGraphDeserializer* d) {
for (intptr_t i = 0, n = InputCount(); i < n; ++i) {
SetInputAt(i, d->Read<Value*>());
}
for (intptr_t i = InputCount() - 1; i >= 0; --i) {
Value* input = InputAt(i);
input->definition()->AddInputUse(input);
}
ReadExtraWithoutInputs(d);
}
void Instruction::WriteExtraWithoutInputs(FlowGraphSerializer* s) {
s->Write<Environment*>(env_);
s->Write<LocationSummary*>(locs_);
}
void Instruction::ReadExtraWithoutInputs(FlowGraphDeserializer* d) {
Environment* env = d->Read<Environment*>();
SetEnvironment(env);
locs_ = d->Read<LocationSummary*>();
}
#define INSTRUCTIONS_SERIALIZABLE_AS_INSTRUCTION(V) \
V(Condition, ConditionInstr) \
V(Constant, ConstantInstr) \
V(Definition, Definition) \
V(ParallelMove, ParallelMoveInstr) \
V(Phi, PhiInstr)
#define SERIALIZABLE_AS_INSTRUCTION(name, type) \
template <> \
void FlowGraphSerializer::WriteTrait<type*>::Write(FlowGraphSerializer* s, \
type* x) { \
s->Write<Instruction*>(x); \
} \
template <> \
type* FlowGraphDeserializer::ReadTrait<type*>::Read( \
FlowGraphDeserializer* d) { \
Instruction* instr = d->Read<Instruction*>(); \
ASSERT((instr == nullptr) || instr->Is##name()); \
return static_cast<type*>(instr); \
}
INSTRUCTIONS_SERIALIZABLE_AS_INSTRUCTION(SERIALIZABLE_AS_INSTRUCTION)
#undef SERIALIZABLE_AS_INSTRUCTION
#undef INSTRUCTIONS_SERIALIZABLE_AS_INSTRUCTION
template <>
void FlowGraphSerializer::WriteTrait<int8_t>::Write(FlowGraphSerializer* s,
int8_t x) {
s->stream()->Write<int8_t>(x);
}
template <>
int8_t FlowGraphDeserializer::ReadTrait<int8_t>::Read(
FlowGraphDeserializer* d) {
return d->stream()->Read<int8_t>();
}
template <>
void FlowGraphSerializer::WriteTrait<int16_t>::Write(FlowGraphSerializer* s,
int16_t x) {
s->stream()->Write<int16_t>(x);
}
template <>
int16_t FlowGraphDeserializer::ReadTrait<int16_t>::Read(
FlowGraphDeserializer* d) {
return d->stream()->Read<int16_t>();
}
template <>
void FlowGraphSerializer::WriteTrait<int32_t>::Write(FlowGraphSerializer* s,
int32_t x) {
s->stream()->Write<int32_t>(x);
}
template <>
int32_t FlowGraphDeserializer::ReadTrait<int32_t>::Read(
FlowGraphDeserializer* d) {
return d->stream()->Read<int32_t>();
}
template <>
void FlowGraphSerializer::WriteTrait<int64_t>::Write(FlowGraphSerializer* s,
int64_t x) {
s->stream()->Write<int64_t>(x);
}
template <>
int64_t FlowGraphDeserializer::ReadTrait<int64_t>::Read(
FlowGraphDeserializer* d) {
return d->stream()->Read<int64_t>();
}
void JoinEntryInstr::WriteExtra(FlowGraphSerializer* s) {
BlockEntryInstr::WriteExtra(s);
if (phis_ != nullptr) {
for (PhiInstr* phi : *phis_) {
phi->WriteExtra(s);
}
}
}
void JoinEntryInstr::ReadExtra(FlowGraphDeserializer* d) {
BlockEntryInstr::ReadExtra(d);
if (phis_ != nullptr) {
for (PhiInstr* phi : *phis_) {
phi->ReadExtra(d);
}
}
}
template <>
void FlowGraphSerializer::WriteTrait<const LocalVariable&>::Write(
FlowGraphSerializer* s,
const LocalVariable& x) {
UNIMPLEMENTED();
}
template <>
const LocalVariable&
FlowGraphDeserializer::ReadTrait<const LocalVariable&>::Read(
FlowGraphDeserializer* d) {
UNIMPLEMENTED();
return *d->parsed_function().receiver_var();
}
void Location::Write(FlowGraphSerializer* s) const {
if (IsPairLocation()) {
s->Write<uword>(value_ & kLocationTagMask);
PairLocation* pair = AsPairLocation();
pair->At(0).Write(s);
pair->At(1).Write(s);
} else if (IsConstant()) {
s->Write<uword>(value_ & kLocationTagMask);
s->WriteRef<Definition*>(constant_instruction());
} else {
s->Write<uword>(value_);
}
}
Location Location::Read(FlowGraphDeserializer* d) {
const uword value = d->Read<uword>();
if (value == kPairLocationTag) {
const Location first = Location::Read(d);
const Location second = Location::Read(d);
return Location::Pair(first, second);
} else if ((value & kConstantTag) == kConstantTag) {
ConstantInstr* instr = d->ReadRef<Definition*>()->AsConstant();
ASSERT(instr != nullptr);
const int pair_index = (value & kPairLocationTag) != 0 ? 1 : 0;
return Location::Constant(instr, pair_index);
} else {
return Location(value);
}
}
template <>
void FlowGraphSerializer::WriteTrait<LocationSummary*>::Write(
FlowGraphSerializer* s,
LocationSummary* x) {
ASSERT(s->can_write_refs());
if (x == nullptr) {
s->Write<bool>(false);
} else {
s->Write<bool>(true);
x->Write(s);
}
}
template <>
LocationSummary* FlowGraphDeserializer::ReadTrait<LocationSummary*>::Read(
FlowGraphDeserializer* d) {
if (!d->Read<bool>()) {
return nullptr;
}
return new (d->zone()) LocationSummary(d);
}
void LocationSummary::Write(FlowGraphSerializer* s) const {
s->Write<intptr_t>(input_count());
s->Write<intptr_t>(temp_count());
s->Write<int8_t>(static_cast<int8_t>(contains_call_));
live_registers_.Write(s);
for (intptr_t i = 0, n = input_count(); i < n; ++i) {
in(i).Write(s);
}
for (intptr_t i = 0, n = temp_count(); i < n; ++i) {
temp(i).Write(s);
}
ASSERT(output_count() == 1);
out(0).Write(s);
if ((stack_bitmap_ != nullptr) && (stack_bitmap_->Length() != 0)) {
s->Write<int8_t>(1);
stack_bitmap_->Write(s->stream());
} else {
s->Write<int8_t>(0);
}
#if defined(DEBUG)
s->Write<intptr_t>(writable_inputs_);
#endif
}
LocationSummary::LocationSummary(FlowGraphDeserializer* d)
: num_inputs_(d->Read<intptr_t>()),
num_temps_(d->Read<intptr_t>()),
output_location_(),
stack_bitmap_(nullptr),
contains_call_(static_cast<ContainsCall>(d->Read<int8_t>())),
live_registers_(d) {
input_locations_ = d->zone()->Alloc<Location>(num_inputs_);
for (intptr_t i = 0; i < num_inputs_; ++i) {
input_locations_[i] = Location::Read(d);
}
temp_locations_ = d->zone()->Alloc<Location>(num_temps_);
for (intptr_t i = 0; i < num_temps_; ++i) {
temp_locations_[i] = Location::Read(d);
}
output_location_ = Location::Read(d);
if (d->Read<int8_t>() != 0) {
EnsureStackBitmap().Read(d->stream());
}
#if defined(DEBUG)
writable_inputs_ = d->Read<intptr_t>();
#endif
}
void MakeTempInstr::WriteExtra(FlowGraphSerializer* s) {
TemplateDefinition::WriteExtra(s);
null_->WriteExtra(s);
}
void MakeTempInstr::ReadExtra(FlowGraphDeserializer* d) {
TemplateDefinition::ReadExtra(d);
null_->ReadExtra(d);
}
void MaterializeObjectInstr::WriteExtra(FlowGraphSerializer* s) {
VariadicDefinition::WriteExtra(s);
for (intptr_t i = 0, n = InputCount(); i < n; ++i) {
locations_[i].Write(s);
}
}
void MaterializeObjectInstr::ReadExtra(FlowGraphDeserializer* d) {
VariadicDefinition::ReadExtra(d);
locations_ = d->zone()->Alloc<Location>(InputCount());
for (intptr_t i = 0, n = InputCount(); i < n; ++i) {
locations_[i] = Location::Read(d);
}
}
template <>
void FlowGraphSerializer::WriteTrait<MoveOperands*>::Write(
FlowGraphSerializer* s,
MoveOperands* x) {
x->Write(s);
}
template <>
MoveOperands* FlowGraphDeserializer::ReadTrait<MoveOperands*>::Read(
FlowGraphDeserializer* d) {
return new (d->zone()) MoveOperands(d);
}
void MoveOperands::Write(FlowGraphSerializer* s) const {
dest().Write(s);
src().Write(s);
}
MoveOperands::MoveOperands(FlowGraphDeserializer* d)
: dest_(Location::Read(d)), src_(Location::Read(d)) {}
void FlowGraphSerializer::AddBaseObject(const Object& x) {
const intptr_t object_index = object_counter_++;
heap()->SetObjectId(x.ptr(), object_index + 1);
}
template <>
void FlowGraphSerializer::WriteTrait<const Object&>::Write(
FlowGraphSerializer* s,
const Object& x) {
const intptr_t cid = x.GetClassId();
ASSERT(cid != kIllegalCid);
// Do not write objects repeatedly.
const intptr_t object_id = s->heap()->GetObjectId(x.ptr());
if (object_id > 0) {
const intptr_t object_index = object_id - 1;
s->Write<intptr_t>(kIllegalCid);
s->Write<intptr_t>(object_index);
return;
}
const intptr_t object_index = s->object_counter_++;
s->heap()->SetObjectId(x.ptr(), object_index + 1);
s->Write<intptr_t>(cid);
s->WriteObjectImpl(x, cid, object_index);
}
void FlowGraphDeserializer::AddBaseObject(const Object& x) {
const intptr_t object_index = object_counter_;
object_counter_++;
SetObjectAt(object_index, x);
}
template <>
const Object& FlowGraphDeserializer::ReadTrait<const Object&>::Read(
FlowGraphDeserializer* d) {
const intptr_t cid = d->Read<intptr_t>();
if (cid == kIllegalCid) {
const intptr_t object_index = d->Read<intptr_t>();
return *(d->objects_[object_index]);
}
const intptr_t object_index = d->object_counter_;
d->object_counter_++;
const Object& result = d->ReadObjectImpl(cid, object_index);
d->SetObjectAt(object_index, result);
return result;
}
void FlowGraphDeserializer::SetObjectAt(intptr_t object_index,
const Object& object) {
objects_.EnsureLength(object_index + 1, &Object::null_object());
objects_[object_index] = &object;
}
bool FlowGraphSerializer::IsWritten(const Object& obj) {
const intptr_t object_id = heap()->GetObjectId(obj.ptr());
return (object_id != 0);
}
bool FlowGraphSerializer::HasEnclosingTypes(const Object& obj) {
if (num_free_fun_type_params_ == 0) return false;
if (obj.IsAbstractType()) {
return !AbstractType::Cast(obj).IsInstantiated(kFunctions,
num_free_fun_type_params_);
} else if (obj.IsTypeArguments()) {
return !TypeArguments::Cast(obj).IsInstantiated(kFunctions,
num_free_fun_type_params_);
} else {
UNREACHABLE();
}
}
bool FlowGraphSerializer::WriteObjectWithEnclosingTypes(const Object& obj) {
if (HasEnclosingTypes(obj)) {
Write<bool>(true);
// Reset assigned object id so it could be written
// while writing enclosing types.
heap()->SetObjectId(obj.ptr(), -1);
WriteEnclosingTypes(obj, num_free_fun_type_params_);
Write<bool>(false);
// Can write any type parameters after all enclosing types are written.
const intptr_t saved_num_free_fun_type_params = num_free_fun_type_params_;
num_free_fun_type_params_ = 0;
Write<const Object&>(obj);
num_free_fun_type_params_ = saved_num_free_fun_type_params;
return true;
} else {
Write<bool>(false);
return false;
}
}
void FlowGraphSerializer::WriteEnclosingTypes(
const Object& obj,
intptr_t num_free_fun_type_params) {
if (obj.IsType()) {
const auto& type = Type::Cast(obj);
if (type.arguments() != TypeArguments::null()) {
const auto& type_args = TypeArguments::Handle(Z, type.arguments());
WriteEnclosingTypes(type_args, num_free_fun_type_params);
}
} else if (obj.IsRecordType()) {
const auto& rec = RecordType::Cast(obj);
auto& elem = AbstractType::Handle(Z);
for (intptr_t i = 0, n = rec.NumFields(); i < n; ++i) {
elem = rec.FieldTypeAt(i);
WriteEnclosingTypes(elem, num_free_fun_type_params);
}
} else if (obj.IsFunctionType()) {
const auto& sig = FunctionType::Cast(obj);
const intptr_t num_parent_type_args = sig.NumParentTypeArguments();
if (num_free_fun_type_params > num_parent_type_args) {
num_free_fun_type_params = num_parent_type_args;
}
AbstractType& elem = AbstractType::Handle(Z, sig.result_type());
WriteEnclosingTypes(elem, num_free_fun_type_params);
for (intptr_t i = 0, n = sig.NumParameters(); i < n; ++i) {
elem = sig.ParameterTypeAt(i);
WriteEnclosingTypes(elem, num_free_fun_type_params);
}
if (sig.IsGeneric()) {
const TypeParameters& type_params =
TypeParameters::Handle(Z, sig.type_parameters());
WriteEnclosingTypes(TypeArguments::Handle(Z, type_params.bounds()),
num_free_fun_type_params);
}
} else if (obj.IsTypeParameter()) {
const auto& tp = TypeParameter::Cast(obj);
if (tp.IsFunctionTypeParameter() &&
(tp.index() < num_free_fun_type_params)) {
const auto& owner =
FunctionType::Handle(Z, tp.parameterized_function_type());
if (!IsWritten(owner)) {
Write<bool>(true);
Write<const Object&>(owner);
}
}
} else if (obj.IsTypeArguments()) {
const auto& type_args = TypeArguments::Cast(obj);
auto& elem = AbstractType::Handle(Z);
for (intptr_t i = 0, n = type_args.Length(); i < n; ++i) {
elem = type_args.TypeAt(i);
WriteEnclosingTypes(elem, num_free_fun_type_params);
}
}
}
const Object& FlowGraphDeserializer::ReadObjectWithEnclosingTypes() {
if (Read<bool>()) {
while (Read<bool>()) {
Read<const Object&>();
}
return Read<const Object&>();
} else {
return Object::null_object();
}
}
void FlowGraphSerializer::WriteObjectImpl(const Object& x,
intptr_t cid,
intptr_t object_index) {
switch (cid) {
case kArrayCid:
case kImmutableArrayCid: {
const auto& array = Array::Cast(x);
const intptr_t len = array.Length();
Write<intptr_t>(len);
const auto& type_args =
TypeArguments::Handle(Z, array.GetTypeArguments());
Write<const TypeArguments&>(type_args);
if ((len == 0) && type_args.IsNull()) {
break;
}
Write<bool>(array.IsCanonical());
auto& elem = Object::Handle(Z);
for (intptr_t i = 0; i < len; ++i) {
elem = array.At(i);
Write<const Object&>(elem);
}
break;
}
case kBoolCid:
Write<bool>(Bool::Cast(x).value());
break;
case kClosureCid: {
const auto& closure = Closure::Cast(x);
if (closure.RawContext() != Object::null()) {
UNIMPLEMENTED();
}
ASSERT(closure.IsCanonical());
auto& type_args = TypeArguments::Handle(Z);
type_args = closure.instantiator_type_arguments();
Write<const TypeArguments&>(type_args);
type_args = closure.function_type_arguments();
Write<const TypeArguments&>(type_args);
type_args = closure.delayed_type_arguments();
Write<const TypeArguments&>(type_args);
Write<const Function&>(Function::Handle(Z, closure.function()));
break;
}
case kDoubleCid:
ASSERT(x.IsCanonical());
Write<double>(Double::Cast(x).value());
break;
case kFloat32x4Cid:
ASSERT(x.IsCanonical());
Write<simd128_value_t>(Float32x4::Cast(x).value());
break;
case kFloat64x2Cid:
ASSERT(x.IsCanonical());
Write<simd128_value_t>(Float64x2::Cast(x).value());
break;
case kInt32x4Cid:
ASSERT(x.IsCanonical());
Write<simd128_value_t>(Int32x4::Cast(x).value());
break;
case kFieldCid: {
const auto& field = Field::Cast(x);
const auto& owner = Class::Handle(Z, field.Owner());
Write<classid_t>(owner.id());
const intptr_t field_index = owner.FindFieldIndex(field);
ASSERT(field_index >= 0);
Write<intptr_t>(field_index);
break;
}
case kFunctionCid:
Write<const Function&>(Function::Cast(x));
break;
case kFunctionTypeCid: {
const auto& type = FunctionType::Cast(x);
ASSERT(type.IsFinalized());
if (WriteObjectWithEnclosingTypes(type)) {
break;
}
const intptr_t saved_num_free_fun_type_params = num_free_fun_type_params_;
const intptr_t num_parent_type_args = type.NumParentTypeArguments();
if (num_free_fun_type_params_ > num_parent_type_args) {
num_free_fun_type_params_ = num_parent_type_args;
}
Write<int8_t>(static_cast<int8_t>(type.nullability()));
Write<uint32_t>(type.packed_parameter_counts());
Write<uint16_t>(type.packed_type_parameter_counts());
Write<const TypeParameters&>(
TypeParameters::Handle(Z, type.type_parameters()));
Write<const AbstractType&>(AbstractType::Handle(Z, type.result_type()));
Write<const Array&>(Array::Handle(Z, type.parameter_types()));
Write<const Array&>(Array::Handle(Z, type.named_parameter_names()));
num_free_fun_type_params_ = saved_num_free_fun_type_params;
break;
}
case kICDataCid: {
const auto& icdata = ICData::Cast(x);
Write<int8_t>(static_cast<int8_t>(icdata.rebind_rule()));
Write<const Function&>(Function::Handle(Z, icdata.Owner()));
ArgumentsDescriptor(Array::Handle(Z, icdata.arguments_descriptor()))
.Write(this);
Write<intptr_t>(icdata.deopt_id());
Write<intptr_t>(icdata.NumArgsTested());
if (icdata.rebind_rule() == ICData::kStatic) {
ASSERT(icdata.NumberOfChecks() == 1);
Write<const Function&>(Function::Handle(Z, icdata.GetTargetAt(0)));
} else if (icdata.rebind_rule() == ICData::kInstance) {
if (icdata.NumberOfChecks() != 0) {
UNIMPLEMENTED();
}
Write<const String&>(String::Handle(Z, icdata.target_name()));
} else {
UNIMPLEMENTED();
}
break;
}
case kConstMapCid:
case kConstSetCid: {
const auto& map = LinkedHashBase::Cast(x);
ASSERT(map.IsCanonical());
const intptr_t length = map.Length();
Write<intptr_t>(length);
Write<const TypeArguments&>(
TypeArguments::Handle(Z, map.GetTypeArguments()));
const auto& data = Array::Handle(Z, map.data());
auto& elem = Object::Handle(Z);
intptr_t used_data;
if (cid == kConstMapCid) {
used_data = length << 1;
} else {
used_data = length;
}
for (intptr_t i = 0; i < used_data; ++i) {
elem = data.At(i);
Write<const Object&>(elem);
}
break;
}
case kLibraryPrefixCid: {
const auto& prefix = LibraryPrefix::Cast(x);
const Library& library = Library::Handle(Z, prefix.importer());
Write<classid_t>(Class::Handle(Z, library.toplevel_class()).id());
Write<const String&>(String::Handle(Z, prefix.name()));
break;
}
case kMintCid:
ASSERT(x.IsCanonical());
Write<int64_t>(Integer::Cast(x).Value());
break;
case kNullCid:
break;
case kOneByteStringCid: {
ASSERT(x.IsCanonical());
const auto& str = String::Cast(x);
const intptr_t length = str.Length();
Write<intptr_t>(length);
NoSafepointScope no_safepoint;
uint8_t* latin1 = OneByteString::DataStart(str);
stream_->WriteBytes(latin1, length);
break;
}
case kRecordCid: {
ASSERT(x.IsCanonical());
const auto& record = Record::Cast(x);
Write<RecordShape>(record.shape());
auto& field = Object::Handle(Z);
for (intptr_t i = 0, n = record.num_fields(); i < n; ++i) {
field = record.FieldAt(i);
Write<const Object&>(field);
}
break;
}
case kRecordTypeCid: {
const auto& rec = RecordType::Cast(x);
ASSERT(rec.IsFinalized());
if (WriteObjectWithEnclosingTypes(rec)) {
break;
}
Write<int8_t>(static_cast<int8_t>(rec.nullability()));
Write<RecordShape>(rec.shape());
Write<const Array&>(Array::Handle(Z, rec.field_types()));
break;
}
case kSentinelCid:
if (x.ptr() == Object::sentinel().ptr()) {
Write<uint8_t>(0);
} else if (x.ptr() == Object::optimized_out().ptr()) {
Write<uint8_t>(2);
} else {
UNIMPLEMENTED();
}
break;
case kSmiCid:
Write<intptr_t>(Smi::Cast(x).Value());
break;
case kTwoByteStringCid: {
ASSERT(x.IsCanonical());
const auto& str = String::Cast(x);
const intptr_t length = str.Length();
Write<intptr_t>(length);
NoSafepointScope no_safepoint;
uint16_t* utf16 = TwoByteString::DataStart(str);
stream_->WriteBytes(reinterpret_cast<const uint8_t*>(utf16),
length * sizeof(uint16_t));
break;
}
case kTypeCid: {
const auto& type = Type::Cast(x);
ASSERT(type.IsFinalized());
if (WriteObjectWithEnclosingTypes(type)) {
break;
}
const auto& cls = Class::Handle(Z, type.type_class());
Write<int8_t>(static_cast<int8_t>(type.nullability()));
Write<classid_t>(type.type_class_id());
if (cls.IsGeneric()) {
const auto& type_args = TypeArguments::Handle(Z, type.arguments());
Write<const TypeArguments&>(type_args);
}
break;
}
case kTypeArgumentsCid: {
const auto& type_args = TypeArguments::Cast(x);
ASSERT(type_args.IsFinalized());
if (WriteObjectWithEnclosingTypes(type_args)) {
break;
}
const intptr_t len = type_args.Length();
Write<intptr_t>(len);
auto& type = AbstractType::Handle(Z);
for (intptr_t i = 0; i < len; ++i) {
type = type_args.TypeAt(i);
Write<const AbstractType&>(type);
}
break;
}
case kTypeParameterCid: {
const auto& tp = TypeParameter::Cast(x);
ASSERT(tp.IsFinalized());
if (WriteObjectWithEnclosingTypes(tp)) {
break;
}
Write<intptr_t>(tp.base());
Write<intptr_t>(tp.index());
Write<int8_t>(static_cast<int8_t>(tp.nullability()));
if (tp.IsFunctionTypeParameter()) {
Write<bool>(true);
Write<const FunctionType&>(
FunctionType::Handle(Z, tp.parameterized_function_type()));
} else {
Write<bool>(false);
Write<const Class&>(Class::Handle(Z, tp.parameterized_class()));
}
break;
}
case kTypeParametersCid: {
const auto& tps = TypeParameters::Cast(x);
Write<const Array&>(Array::Handle(Z, tps.names()));
Write<const Array&>(Array::Handle(Z, tps.flags()));
Write<const TypeArguments&>(TypeArguments::Handle(Z, tps.bounds()));
Write<const TypeArguments&>(TypeArguments::Handle(Z, tps.defaults()));
break;
}
default: {
const classid_t cid = x.GetClassId();
if ((cid >= kNumPredefinedCids) || (cid == kInstanceCid)) {
const auto& instance = Instance::Cast(x);
ASSERT(instance.IsCanonical());
const auto& cls =
Class::Handle(Z, isolate_group()->class_table()->At(cid));
const auto unboxed_fields_bitmap =
isolate_group()->class_table()->GetUnboxedFieldsMapAt(cid);
const intptr_t next_field_offset = cls.host_next_field_offset();
auto& obj = Object::Handle(Z);
for (intptr_t offset = Instance::NextFieldOffset();
offset < next_field_offset; offset += kCompressedWordSize) {
if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
if (kCompressedWordSize == 8) {
Write<int64_t>(*reinterpret_cast<int64_t*>(
instance.RawFieldAddrAtOffset(offset)));
} else {
Write<int32_t>(*reinterpret_cast<int32_t*>(
instance.RawFieldAddrAtOffset(offset)));
}
} else {
obj = instance.RawGetFieldAtOffset(offset);
Write<const Object&>(obj);
}
}
break;
}
FATAL("Unimplemented WriteObjectImpl for %s", x.ToCString());
}
}
}
const Object& FlowGraphDeserializer::ReadObjectImpl(intptr_t cid,
intptr_t object_index) {
switch (cid) {
case kArrayCid:
case kImmutableArrayCid: {
const intptr_t len = Read<intptr_t>();
const auto& type_args = Read<const TypeArguments&>();
if ((len == 0) && type_args.IsNull()) {
return Object::empty_array();
}
const bool canonicalize = Read<bool>();
auto& array = Array::ZoneHandle(
Z, Array::New(len, canonicalize ? Heap::kNew : Heap::kOld));
if (!type_args.IsNull()) {
array.SetTypeArguments(type_args);
}
for (intptr_t i = 0; i < len; ++i) {
array.SetAt(i, Read<const Object&>());
}
if (cid == kImmutableArrayCid) {
array.MakeImmutable();
}
if (canonicalize) {
array ^= array.Canonicalize(thread());
}
return array;
}
case kBoolCid:
return Bool::Get(Read<bool>());
case kClosureCid: {
const auto& instantiator_type_arguments = Read<const TypeArguments&>();
const auto& function_type_arguments = Read<const TypeArguments&>();
const auto& delayed_type_arguments = Read<const TypeArguments&>();
const auto& function = Read<const Function&>();
auto& closure = Closure::ZoneHandle(
Z, Closure::New(instantiator_type_arguments, function_type_arguments,
delayed_type_arguments, function,
Object::null_object()));
closure ^= closure.Canonicalize(thread());
return closure;
}
case kDoubleCid:
return Double::ZoneHandle(Z, Double::NewCanonical(Read<double>()));
case kFloat32x4Cid: {
auto& simd_value =
Float32x4::ZoneHandle(Z, Float32x4::New(Read<simd128_value_t>()));
simd_value ^= simd_value.Canonicalize(thread());
return simd_value;
}
case kFloat64x2Cid: {
auto& simd_value =
Float64x2::ZoneHandle(Z, Float64x2::New(Read<simd128_value_t>()));
simd_value ^= simd_value.Canonicalize(thread());
return simd_value;
}
case kInt32x4Cid: {
auto& simd_value =
Int32x4::ZoneHandle(Z, Int32x4::New(Read<simd128_value_t>()));
simd_value ^= simd_value.Canonicalize(thread());
return simd_value;
}
case kFieldCid: {
const classid_t owner_class_id = Read<classid_t>();
const intptr_t field_index = Read<intptr_t>();
const auto& owner = Class::Handle(Z, GetClassById(owner_class_id));
auto& result = Field::ZoneHandle(Z, owner.FieldFromIndex(field_index));
ASSERT(!result.IsNull());
return result;
}
case kFunctionCid:
return Read<const Function&>();
case kFunctionTypeCid: {
const auto& enc_type = ReadObjectWithEnclosingTypes();
if (!enc_type.IsNull()) {
return enc_type;
}
const Nullability nullability = static_cast<Nullability>(Read<int8_t>());
auto& result =
FunctionType::ZoneHandle(Z, FunctionType::New(0, nullability));
SetObjectAt(object_index, result);
result.set_packed_parameter_counts(Read<uint32_t>());
result.set_packed_type_parameter_counts(Read<uint16_t>());
result.SetTypeParameters(Read<const TypeParameters&>());
result.set_result_type(Read<const AbstractType&>());
result.set_parameter_types(Read<const Array&>());
result.set_named_parameter_names(Read<const Array&>());
result.SetIsFinalized();
result ^= result.Canonicalize(thread());
return result;
}
case kICDataCid: {
const ICData::RebindRule rebind_rule =
static_cast<ICData::RebindRule>(Read<int8_t>());
const auto& owner = Read<const Function&>();
const auto& arguments_descriptor =
Array::Handle(Z, ArgumentsDescriptor::Read(this));
const intptr_t deopt_id = Read<intptr_t>();
const intptr_t num_args_tested = Read<intptr_t>();
if (rebind_rule == ICData::kStatic) {
const auto& target = Read<const Function&>();
return ICData::ZoneHandle(
Z,
ICData::NewForStaticCall(owner, target, arguments_descriptor,
deopt_id, num_args_tested, rebind_rule));
} else if (rebind_rule == ICData::kInstance) {
const auto& target_name = Read<const String&>();
return ICData::ZoneHandle(
Z, ICData::New(owner, target_name, arguments_descriptor, deopt_id,
num_args_tested, rebind_rule));
} else {
UNIMPLEMENTED();
}
break;
}
case kConstMapCid:
case kConstSetCid: {
const intptr_t length = Read<intptr_t>();
const auto& type_args = Read<const TypeArguments&>();
Instance& result = Instance::ZoneHandle(Z);
intptr_t used_data;
if (cid == kConstMapCid) {
result = ConstMap::NewUninitialized(Heap::kOld);
used_data = (length << 1);
} else {
result = ConstSet::NewUninitialized(Heap::kOld);
used_data = length;
}
// LinkedHashBase is not a proper handle type, so
// cannot create a LinkedHashBase handle upfront.
const LinkedHashBase& map = LinkedHashBase::Cast(result);
map.SetTypeArguments(type_args);
map.set_used_data(used_data);
const auto& data = Array::Handle(Z, Array::New(used_data));
map.set_data(data);
map.set_deleted_keys(0);
map.ComputeAndSetHashMask();
for (intptr_t i = 0; i < used_data; ++i) {
data.SetAt(i, Read<const Object&>());
}
result ^= result.Canonicalize(thread());
return result;
}
case kLibraryPrefixCid: {
const Class& toplevel_class =
Class::Handle(Z, GetClassById(Read<classid_t>()));
const Library& library = Library::Handle(Z, toplevel_class.library());
const String& name = Read<const String&>();
const auto& prefix =
LibraryPrefix::ZoneHandle(Z, library.LookupLocalLibraryPrefix(name));
ASSERT(!prefix.IsNull());
return prefix;
}
case kMintCid: {
const int64_t value = Read<int64_t>();
return Integer::ZoneHandle(Z, Integer::NewCanonical(value));
}
case kNullCid:
return Object::null_object();
case kOneByteStringCid: {
const intptr_t length = Read<intptr_t>();
uint8_t* latin1 = Z->Alloc<uint8_t>(length);
stream_->ReadBytes(latin1, length);
return String::ZoneHandle(Z,
Symbols::FromLatin1(thread(), latin1, length));
}
case kRecordCid: {
const RecordShape shape = Read<RecordShape>();
auto& record = Record::ZoneHandle(Z, Record::New(shape));
for (intptr_t i = 0, n = shape.num_fields(); i < n; ++i) {
record.SetFieldAt(i, Read<const Object&>());
}
record ^= record.Canonicalize(thread());
return record;
}
case kRecordTypeCid: {
const auto& enc_type = ReadObjectWithEnclosingTypes();
if (!enc_type.IsNull()) {
return enc_type;
}
const Nullability nullability = static_cast<Nullability>(Read<int8_t>());
const RecordShape shape = Read<RecordShape>();
const Array& field_types = Read<const Array&>();
RecordType& rec = RecordType::ZoneHandle(
Z, RecordType::New(shape, field_types, nullability));
rec.SetIsFinalized();
rec ^= rec.Canonicalize(thread());
return rec;
}
case kSentinelCid:
switch (Read<uint8_t>()) {
case 0:
return Object::sentinel();
case 2:
return Object::optimized_out();
default:
UNREACHABLE();
}
case kSmiCid:
return Smi::ZoneHandle(Z, Smi::New(Read<intptr_t>()));
case kTwoByteStringCid: {
const intptr_t length = Read<intptr_t>();
uint16_t* utf16 = Z->Alloc<uint16_t>(length);
stream_->ReadBytes(reinterpret_cast<uint8_t*>(utf16),
length * sizeof(uint16_t));
return String::ZoneHandle(Z, Symbols::FromUTF16(thread(), utf16, length));
}
case kTypeCid: {
const auto& enc_type = ReadObjectWithEnclosingTypes();
if (!enc_type.IsNull()) {
return enc_type;
}
const Nullability nullability = static_cast<Nullability>(Read<int8_t>());
const classid_t type_class_id = Read<classid_t>();
const auto& cls = Class::Handle(Z, GetClassById(type_class_id));
auto& result = Type::ZoneHandle(Z);
if (cls.IsGeneric()) {
result = Type::New(cls, Object::null_type_arguments(), nullability);
SetObjectAt(object_index, result);
const auto& type_args = Read<const TypeArguments&>();
result.set_arguments(type_args);
result.SetIsFinalized();
} else {
result = cls.DeclarationType();
result = result.ToNullability(nullability, Heap::kOld);
}
result ^= result.Canonicalize(thread());
return result;
}
case kTypeArgumentsCid: {
const auto& enc_type_args = ReadObjectWithEnclosingTypes();
if (!enc_type_args.IsNull()) {
return enc_type_args;
}
const intptr_t len = Read<intptr_t>();
auto& type_args = TypeArguments::ZoneHandle(Z, TypeArguments::New(len));
SetObjectAt(object_index, type_args);
for (intptr_t i = 0; i < len; ++i) {
type_args.SetTypeAt(i, Read<const AbstractType&>());
}
type_args ^= type_args.Canonicalize(thread());
return type_args;
}
case kTypeParameterCid: {
const auto& enc_type = ReadObjectWithEnclosingTypes();
if (!enc_type.IsNull()) {
return enc_type;
}
const intptr_t base = Read<intptr_t>();
const intptr_t index = Read<intptr_t>();
const Nullability nullability = static_cast<Nullability>(Read<int8_t>());
const Object* owner = nullptr;
if (Read<bool>()) {
owner = &Read<const FunctionType&>();
} else {
owner = &Read<const Class&>();
}
auto& tp = TypeParameter::ZoneHandle(
Z, TypeParameter::New(*owner, base, index, nullability));
SetObjectAt(object_index, tp);
tp.SetIsFinalized();
tp ^= tp.Canonicalize(thread());
return tp;
}
case kTypeParametersCid: {
const auto& tps = TypeParameters::ZoneHandle(Z, TypeParameters::New());
tps.set_names(Read<const Array&>());
tps.set_flags(Read<const Array&>());
tps.set_bounds(Read<const TypeArguments&>());
tps.set_defaults(Read<const TypeArguments&>());
return tps;
}
default:
if ((cid >= kNumPredefinedCids) || (cid == kInstanceCid)) {
const auto& cls = Class::Handle(Z, GetClassById(cid));
const auto unboxed_fields_bitmap =
isolate_group()->class_table()->GetUnboxedFieldsMapAt(cid);
const intptr_t next_field_offset = cls.host_next_field_offset();
auto& instance = Instance::ZoneHandle(Z, Instance::New(cls));
for (intptr_t offset = Instance::NextFieldOffset();
offset < next_field_offset; offset += kCompressedWordSize) {
if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
if (kCompressedWordSize == 8) {
const int64_t v = Read<int64_t>();
*reinterpret_cast<int64_t*>(
instance.RawFieldAddrAtOffset(offset)) = v;
} else {
const int32_t v = Read<int32_t>();
*reinterpret_cast<int32_t*>(
instance.RawFieldAddrAtOffset(offset)) = v;
}
} else {
const auto& obj = Read<const Object&>();
instance.RawSetFieldAtOffset(offset, obj);
}
}
instance = instance.Canonicalize(thread());
return instance;
}
}
UNIMPLEMENTED();
return Object::null_object();
}
#define HANDLES_SERIALIZABLE_AS_OBJECT(V) \
V(AbstractType, Object::null_abstract_type()) \
V(Array, Object::null_array()) \
V(Field, Field::Handle(d->zone())) \
V(FunctionType, Object::null_function_type()) \
V(Instance, Object::null_instance()) \
V(String, Object::null_string()) \
V(TypeArguments, Object::null_type_arguments()) \
V(TypeParameters, TypeParameters::Handle(d->zone()))
#define SERIALIZE_HANDLE_AS_OBJECT(handle, null_handle) \
template <> \
void FlowGraphSerializer::WriteTrait<const handle&>::Write( \
FlowGraphSerializer* s, const handle& x) { \
s->Write<const Object&>(x); \
} \
template <> \
const handle& FlowGraphDeserializer::ReadTrait<const handle&>::Read( \
FlowGraphDeserializer* d) { \
const Object& result = d->Read<const Object&>(); \
if (result.IsNull()) { \
return null_handle; \
} \
return handle::Cast(result); \
}
HANDLES_SERIALIZABLE_AS_OBJECT(SERIALIZE_HANDLE_AS_OBJECT)
#undef SERIALIZE_HANDLE_AS_OBJECT
#undef HANDLES_SERIALIZABLE_AS_OBJECT
void OsrEntryInstr::WriteTo(FlowGraphSerializer* s) {
BlockEntryWithInitialDefs::WriteTo(s);
}
OsrEntryInstr::OsrEntryInstr(FlowGraphDeserializer* d)
: BlockEntryWithInitialDefs(d), graph_entry_(d->graph_entry()) {}
void ParallelMoveInstr::WriteExtra(FlowGraphSerializer* s) {
Instruction::WriteExtra(s);
s->Write<GrowableArray<MoveOperands*>>(moves_);
s->Write<const MoveSchedule*>(move_schedule_);
}
void ParallelMoveInstr::ReadExtra(FlowGraphDeserializer* d) {
Instruction::ReadExtra(d);
moves_ = d->Read<GrowableArray<MoveOperands*>>();
move_schedule_ = d->Read<const MoveSchedule*>();
}
void ParameterInstr::WriteExtra(FlowGraphSerializer* s) {
TemplateDefinition::WriteExtra(s);
location_.Write(s);
}
void ParameterInstr::ReadExtra(FlowGraphDeserializer* d) {
TemplateDefinition::ReadExtra(d);
location_ = Location::Read(d);
}
void PhiInstr::WriteTo(FlowGraphSerializer* s) {
VariadicDefinition::WriteTo(s);
s->Write<Representation>(representation_);
s->Write<bool>(is_alive_);
s->Write<int8_t>(is_receiver_);
}
PhiInstr::PhiInstr(FlowGraphDeserializer* d)
: VariadicDefinition(d),
block_(d->current_block()->AsJoinEntry()),
representation_(d->Read<Representation>()),
is_alive_(d->Read<bool>()),
is_receiver_(d->Read<int8_t>()) {}
void LeafRuntimeCallInstr::WriteTo(FlowGraphSerializer* s) {
VariadicDefinition::WriteTo(s);
s->Write<Representation>(return_representation_);
s->Write<const ZoneGrowableArray<Representation>&>(argument_representations_);
}
LeafRuntimeCallInstr::LeafRuntimeCallInstr(FlowGraphDeserializer* d)
: VariadicDefinition(d),
return_representation_(d->Read<Representation>()),
argument_representations_(
d->Read<const ZoneGrowableArray<Representation>&>()),
native_calling_convention_(
compiler::ffi::NativeCallingConvention::FromSignature(
d->zone(),
*compiler::ffi::NativeFunctionType::FromRepresentations(
d->zone(),
return_representation_,
argument_representations_))) {}
template <>
void FlowGraphSerializer::WriteTrait<Range*>::Write(FlowGraphSerializer* s,
Range* x) {
if (x == nullptr) {
s->Write<bool>(false);
} else {
s->Write<bool>(true);
x->Write(s);
}
}
template <>
Range* FlowGraphDeserializer::ReadTrait<Range*>::Read(
FlowGraphDeserializer* d) {
if (!d->Read<bool>()) {
return nullptr;
}
return new (d->zone()) Range(d);
}
void Range::Write(FlowGraphSerializer* s) const {
// Drop symbolic range boundaries - they are not useful
// when flow graph is serialized/deserialized.
min_.LowerBound().Write(s);
max_.UpperBound().Write(s);
}
Range::Range(FlowGraphDeserializer* d)
: min_(RangeBoundary(d)), max_(RangeBoundary(d)) {}
void RangeBoundary::Write(FlowGraphSerializer* s) const {
ASSERT(!IsSymbol());
s->Write<int8_t>(kind_);
s->Write<int64_t>(value_);
s->Write<int64_t>(offset_);
}
RangeBoundary::RangeBoundary(FlowGraphDeserializer* d)
: kind_(static_cast<Kind>(d->Read<int8_t>())),
value_(d->Read<int64_t>()),
offset_(d->Read<int64_t>()) {}
template <>
void FlowGraphSerializer::WriteTrait<RecordShape>::Write(FlowGraphSerializer* s,
RecordShape x) {
s->Write<intptr_t>(x.num_fields());
s->Write<const Array&>(
Array::Handle(s->zone(), x.GetFieldNames(s->thread())));
}
template <>
RecordShape FlowGraphDeserializer::ReadTrait<RecordShape>::Read(
FlowGraphDeserializer* d) {
const intptr_t num_fields = d->Read<intptr_t>();
const auto& field_names = d->Read<const Array&>();
return RecordShape::Register(d->thread(), num_fields, field_names);
}
void RegisterSet::Write(FlowGraphSerializer* s) const {
s->Write<uintptr_t>(cpu_registers_.data());
s->Write<uintptr_t>(untagged_cpu_registers_.data());
s->Write<uintptr_t>(fpu_registers_.data());
}
RegisterSet::RegisterSet(FlowGraphDeserializer* d)
: cpu_registers_(d->Read<uintptr_t>()),
untagged_cpu_registers_(d->Read<uintptr_t>()),
fpu_registers_(d->Read<uintptr_t>()) {}
template <>
void FlowGraphSerializer::WriteTrait<Representation>::Write(
FlowGraphSerializer* s,
Representation x) {
s->Write<uint8_t>(x);
}
template <>
Representation FlowGraphDeserializer::ReadTrait<Representation>::Read(
FlowGraphDeserializer* d) {
return static_cast<Representation>(d->Read<uint8_t>());
}
template <>
void FlowGraphSerializer::WriteTrait<simd128_value_t>::Write(
FlowGraphSerializer* s,
simd128_value_t x) {
s->stream()->WriteBytes(&x, sizeof(simd128_value_t));
}
template <>
simd128_value_t FlowGraphDeserializer::ReadTrait<simd128_value_t>::Read(
FlowGraphDeserializer* d) {
simd128_value_t value;
d->stream()->ReadBytes(&value, sizeof(simd128_value_t));
return value;
}
template <>
void FlowGraphSerializer::WriteTrait<const Slot&>::Write(FlowGraphSerializer* s,
const Slot& x) {
x.Write(s);
}
template <>
const Slot& FlowGraphDeserializer::ReadTrait<const Slot&>::Read(
FlowGraphDeserializer* d) {
return Slot::Read(d);
}
template <>
void FlowGraphSerializer::WriteTrait<const Slot*>::Write(FlowGraphSerializer* s,
const Slot* x) {
if (x == nullptr) {
s->Write<bool>(false);
return;
}
s->Write<bool>(true);
x->Write(s);
}
template <>
const Slot* FlowGraphDeserializer::ReadTrait<const Slot*>::Read(
FlowGraphDeserializer* d) {
if (!d->Read<bool>()) {
return nullptr;
}
return &Slot::Read(d);
}
void Slot::Write(FlowGraphSerializer* s) const {
s->Write<serializable_type_t<Kind>>(
static_cast<serializable_type_t<Kind>>(kind_));
switch (kind_) {
case Kind::kTypeArguments:
s->Write<int8_t>(flags_);
s->Write<intptr_t>(offset_in_bytes_);
break;
case Kind::kTypeArgumentsIndex:
s->Write<intptr_t>(offset_in_bytes_);
break;
case Kind::kArrayElement:
s->Write<intptr_t>(offset_in_bytes_);
break;
case Kind::kRecordField:
s->Write<intptr_t>(offset_in_bytes_);
break;
case Kind::kCapturedVariable:
s->Write<int8_t>(flags_);
s->Write<intptr_t>(offset_in_bytes_);
s->Write<const String&>(*DataAs<const String>());
type_.Write(s);
break;
case Kind::kDartField:
s->Write<const Field&>(field());
break;
default:
break;
}
}
const Slot& Slot::Read(FlowGraphDeserializer* d) {
const Kind kind = static_cast<Kind>(d->Read<serializable_type_t<Kind>>());
int8_t flags = 0;
intptr_t offset = -1;
const void* data = nullptr;
CompileType type = CompileType::None();
Representation representation = kTagged;
switch (kind) {
case Kind::kTypeArguments:
flags = d->Read<int8_t>();
offset = d->Read<intptr_t>();
data = ":type_arguments";
type = CompileType::FromCid(kTypeArgumentsCid);
break;
case Kind::kTypeArgumentsIndex:
flags =
IsImmutableBit::encode(true) |
IsCompressedBit::encode(TypeArguments::ContainsCompressedPointers());
offset = d->Read<intptr_t>();
data = ":argument";
type = CompileType(CompileType::kCannotBeNull,
CompileType::kCannotBeSentinel, kDynamicCid, nullptr);
break;
case Kind::kArrayElement:
flags = IsCompressedBit::encode(Array::ContainsCompressedPointers());
offset = d->Read<intptr_t>();
data = ":array_element";
type = CompileType::Dynamic();
break;
case Kind::kRecordField:
flags = IsCompressedBit::encode(Record::ContainsCompressedPointers());
offset = d->Read<intptr_t>();
data = ":record_field";
type = CompileType::Dynamic();
break;
case Kind::kCapturedVariable:
flags = d->Read<int8_t>();
offset = d->Read<intptr_t>();
data = &d->Read<const String&>();
type = CompileType(d);
break;
case Kind::kDartField: {
const Field& field = d->Read<const Field&>();
return Slot::Get(field, &d->parsed_function());
}
default:
return Slot::GetNativeSlot(kind);
}
return GetCanonicalSlot(d->thread(), kind, flags, offset, data, type,
representation);
}
template <>
void FlowGraphSerializer::WriteTrait<const compiler::TableSelector*>::Write(
FlowGraphSerializer* s,
const compiler::TableSelector* x) {
#if defined(DART_PRECOMPILER)
ASSERT(x != nullptr);
s->Write<int32_t>(x->id);
#else
UNREACHABLE();
#endif
}
template <>
const compiler::TableSelector*
FlowGraphDeserializer::ReadTrait<const compiler::TableSelector*>::Read(
FlowGraphDeserializer* d) {
#if defined(DART_PRECOMPILER)
const int32_t id = d->Read<int32_t>();
const compiler::TableSelector* selector =
Precompiler::Instance()->selector_map()->GetSelector(id);
ASSERT(selector != nullptr);
return selector;
#else
UNREACHABLE();
#endif
}
template <intptr_t kExtraInputs>
void TemplateDartCall<kExtraInputs>::WriteTo(FlowGraphSerializer* s) {
VariadicDefinition::WriteTo(s);
s->Write<intptr_t>(type_args_len_);
s->Write<const Array&>(argument_names_);
s->Write<TokenPosition>(token_pos_);
if (move_arguments_ == nullptr) {
s->Write<intptr_t>(-1);
} else {
s->Write<intptr_t>(move_arguments_->length());
// Write detached MoveArgument instructions.
for (auto move_arg : *move_arguments_) {
if (move_arg->next() == nullptr) {
s->Write<bool>(true);
s->Write<Instruction*>(move_arg);
} else {
s->Write<bool>(false);
}
}
}
}
template <intptr_t kExtraInputs>
TemplateDartCall<kExtraInputs>::TemplateDartCall(FlowGraphDeserializer* d)
: VariadicDefinition(d),
type_args_len_(d->Read<intptr_t>()),
argument_names_(d->Read<const Array&>()),
token_pos_(d->Read<TokenPosition>()) {
const intptr_t num_move_args = d->Read<intptr_t>();
if (num_move_args >= 0) {
move_arguments_ =
new (d->zone()) MoveArgumentsArray(d->zone(), num_move_args);
move_arguments_->EnsureLength(num_move_args, nullptr);
for (intptr_t i = 0; i < num_move_args; i++) {
if (d->Read<bool>()) {
auto move_arg = d->Read<Instruction*>()->AsMoveArgument();
ASSERT(move_arg != nullptr);
(*move_arguments_)[i] = move_arg;
}
}
}
}
template <intptr_t kExtraInputs>
void TemplateDartCall<kExtraInputs>::WriteExtra(FlowGraphSerializer* s) {
VariadicDefinition::WriteExtra(s);
if (move_arguments_ != nullptr) {
// Write extras for detached MoveArgument in reverse order, because
// we are going to read them back in reverse order.
for (intptr_t i = move_arguments_->length() - 1; i >= 0; --i) {
auto move_arg = move_arguments_->At(i);
if (move_arg->next() == nullptr) {
move_arg->WriteExtra(s);
}
}
}
}
template <intptr_t kExtraInputs>
void TemplateDartCall<kExtraInputs>::ReadExtra(FlowGraphDeserializer* d) {
VariadicDefinition::ReadExtra(d);
if (move_arguments_ != nullptr) {
Instruction* cursor = this;
for (intptr_t i = move_arguments_->length() - 1; i >= 0; --i) {
if ((*move_arguments_)[i] != nullptr) {
(*move_arguments_)[i]->ReadExtra(d);
} else {
// Note: IL might be serialized after ParallelMove instructions
// were inserted between MoveArguments.
do {
cursor = cursor->previous();
} while (!cursor->IsMoveArgument());
(*move_arguments_)[i] = cursor->AsMoveArgument();
}
}
if (env() != nullptr) {
RepairArgumentUsesInEnvironment();
}
}
}
// Explicit template instantiations, needed for the methods above.
template class TemplateDartCall<0>;
template class TemplateDartCall<1>;
void MoveArgumentInstr::WriteExtra(FlowGraphSerializer* s) {
TemplateDefinition::WriteExtra(s);
location_.Write(s);
}
void MoveArgumentInstr::ReadExtra(FlowGraphDeserializer* d) {
TemplateDefinition::ReadExtra(d);
location_ = Location::Read(d);
}
template <>
void FlowGraphSerializer::WriteTrait<TokenPosition>::Write(
FlowGraphSerializer* s,
TokenPosition x) {
s->Write<int32_t>(x.Serialize());
}
template <>
TokenPosition FlowGraphDeserializer::ReadTrait<TokenPosition>::Read(
FlowGraphDeserializer* d) {
return TokenPosition::Deserialize(d->Read<int32_t>());
}
template <>
void FlowGraphSerializer::WriteTrait<uint8_t>::Write(FlowGraphSerializer* s,
uint8_t x) {
s->stream()->Write<uint8_t>(x);
}
template <>
uint8_t FlowGraphDeserializer::ReadTrait<uint8_t>::Read(
FlowGraphDeserializer* d) {
return d->stream()->Read<uint8_t>();
}
template <>
void FlowGraphSerializer::WriteTrait<uint16_t>::Write(FlowGraphSerializer* s,
uint16_t x) {
s->stream()->Write<uint16_t>(x);
}
template <>
uint16_t FlowGraphDeserializer::ReadTrait<uint16_t>::Read(
FlowGraphDeserializer* d) {
return d->stream()->Read<uint16_t>();
}
template <>
void FlowGraphSerializer::WriteTrait<uint32_t>::Write(FlowGraphSerializer* s,
uint32_t x) {
s->stream()->Write<int32_t>(static_cast<int32_t>(x));
}
template <>
uint32_t FlowGraphDeserializer::ReadTrait<uint32_t>::Read(
FlowGraphDeserializer* d) {
return static_cast<uint32_t>(d->stream()->Read<int32_t>());
}
template <>
void FlowGraphSerializer::WriteTrait<uint64_t>::Write(FlowGraphSerializer* s,
uint64_t x) {
s->stream()->Write<int64_t>(static_cast<int64_t>(x));
}
template <>
uint64_t FlowGraphDeserializer::ReadTrait<uint64_t>::Read(
FlowGraphDeserializer* d) {
return static_cast<uint64_t>(d->stream()->Read<int64_t>());
}
template <>
void FlowGraphSerializer::WriteTrait<Value*>::Write(FlowGraphSerializer* s,
Value* x) {
ASSERT(s->can_write_refs());
CompileType* reaching_type = x->reaching_type();
Definition* def = x->definition();
// Omit reaching type if it is the same as definition type.
if ((reaching_type != nullptr) && def->HasType() &&
(reaching_type == def->Type())) {
reaching_type = nullptr;
}
s->Write<CompileType*>(reaching_type);
s->WriteRef<Definition*>(def);
}
template <>
Value* FlowGraphDeserializer::ReadTrait<Value*>::Read(
FlowGraphDeserializer* d) {
CompileType* type = d->Read<CompileType*>();
Definition* def = d->ReadRef<Definition*>();
Value* value = new (d->zone()) Value(def);
value->SetReachingType(type);
return value;
}
void VariadicDefinition::WriteTo(FlowGraphSerializer* s) {
Definition::WriteTo(s);
s->Write<intptr_t>(inputs_.length());
}
VariadicDefinition::VariadicDefinition(FlowGraphDeserializer* d)
: Definition(d), inputs_(d->zone(), 0) {
const intptr_t num_inputs = d->Read<intptr_t>();
inputs_.EnsureLength(num_inputs, nullptr);
}
} // namespace dart