7cd8fda37e
Also load/store canonical hashes in the heap for non-empty TypedData instances in the same manner as canonical hashes for Arrays. TEST=ci (refactoring only) Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try Change-Id: I54274b558fa9f0c8e304198b18cb3f0e9c3e0dfb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/504600 Commit-Queue: Tess Strickland <sstrickl@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
2718 lines
90 KiB
C++
2718 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.
|
|
// Write them as arguments count and query cache during deserialization.
|
|
ASSERT(NamedCount() == 0);
|
|
ASSERT(Count() == Size());
|
|
s->Write<intptr_t>(TypeArgsLen());
|
|
s->Write<intptr_t>(Count());
|
|
} else {
|
|
ASSERT(array_.IsCanonical());
|
|
s->Write<intptr_t>(-1);
|
|
s->Write<const Array&>(array_);
|
|
}
|
|
}
|
|
|
|
ArrayPtr ArgumentsDescriptor::Read(FlowGraphDeserializer* d) {
|
|
const intptr_t num_type_args = d->Read<intptr_t>();
|
|
if (num_type_args < 0) {
|
|
return d->Read<const Array&>().ptr();
|
|
}
|
|
const intptr_t num_args = d->Read<intptr_t>();
|
|
return NewBoxed(num_type_args, 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;
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
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<uint8_t>(flags_);
|
|
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)
|
|
: flags_(d->Read<uint8_t>()), 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 TypedData&>(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 TypedData& coverage_array = Read<const TypedData&>();
|
|
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 kTypedDataUint32ArrayCid: {
|
|
const auto& array = TypedData::Cast(x);
|
|
const intptr_t len = array.Length();
|
|
Write<intptr_t>(len);
|
|
for (intptr_t i = 0; i < len; ++i) {
|
|
uint32_t elem = array.GetUint32(i * kInt32Size);
|
|
Write<uint32_t>(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 kTypedDataUint32ArrayCid: {
|
|
const intptr_t len = Read<intptr_t>();
|
|
if (len == 0) {
|
|
// Currently only used for coverage arrays.
|
|
return Object::empty_coverage_array();
|
|
}
|
|
auto& array = TypedData::ZoneHandle(
|
|
Z, TypedData::New(kTypedDataUint32ArrayCid, len, Heap::kOld));
|
|
for (intptr_t i = 0; i < len; ++i) {
|
|
array.SetUint32(i * kInt32Size, Read<uint32_t>());
|
|
}
|
|
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())) \
|
|
V(TypedData, TypedData::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::kClosureElement:
|
|
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::kClosureElement:
|
|
flags = IsCompressedBit::encode(Closure::ContainsCompressedPointers());
|
|
offset = d->Read<intptr_t>();
|
|
data = OS::SCreate(
|
|
d->zone(), ":closure_element[%" Pd "]",
|
|
compiler::target::Closure::element_index_at_offset(offset));
|
|
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
|