| // 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/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/ffi/call.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()), |
| isolate_group_(IsolateGroup::Current()), |
| heap_(IsolateGroup::Current()->heap()) {} |
| |
| 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()) {} |
| |
| ClassPtr FlowGraphDeserializer::GetClassById(classid_t id) const { |
| return isolate_group()->class_table()->At(id); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const AbstractType*>(const AbstractType* x) { |
| if (x == nullptr) { |
| Write<bool>(false); |
| } else { |
| Write<bool>(true); |
| Write<const AbstractType&>(*x); |
| } |
| } |
| |
| template <> |
| const AbstractType* FlowGraphDeserializer::Read<const AbstractType*>() { |
| if (!Read<bool>()) { |
| return nullptr; |
| } |
| return &Read<const AbstractType&>(); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<AliasIdentity>(AliasIdentity x) { |
| x.Write(this); |
| } |
| |
| template <> |
| AliasIdentity FlowGraphDeserializer::Read<AliasIdentity>() { |
| return AliasIdentity(this); |
| } |
| |
| void AliasIdentity::Write(FlowGraphSerializer* s) const { |
| s->Write<intptr_t>(value_); |
| } |
| |
| AliasIdentity::AliasIdentity(FlowGraphDeserializer* d) |
| : value_(d->Read<intptr_t>()) {} |
| |
| 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::WriteRef<BlockEntryInstr*>(BlockEntryInstr* x) { |
| ASSERT(can_write_refs()); |
| if (x == nullptr) { |
| Write<intptr_t>(-1); |
| return; |
| } |
| const intptr_t id = x->block_id(); |
| ASSERT(id >= 0); |
| Write<intptr_t>(id); |
| } |
| |
| template <> |
| BlockEntryInstr* FlowGraphDeserializer::ReadRef<BlockEntryInstr*>() { |
| const intptr_t id = Read<intptr_t>(); |
| if (id < 0) { |
| return nullptr; |
| } |
| return 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) |
| |
| #define SERIALIZABLE_AS_BLOCK_ENTRY(name, type) \ |
| template <> \ |
| void FlowGraphSerializer::WriteRef<type*>(type * x) { \ |
| WriteRef<BlockEntryInstr*>(x); \ |
| } \ |
| template <> \ |
| type* FlowGraphDeserializer::ReadRef<type*>() { \ |
| BlockEntryInstr* instr = 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::Write<bool>(bool x) { |
| stream_->Write<uint8_t>(x ? 1 : 0); |
| } |
| |
| template <> |
| bool FlowGraphDeserializer::Read<bool>() { |
| return (stream_->Read<uint8_t>() != 0); |
| } |
| |
| void BranchInstr::WriteExtra(FlowGraphSerializer* s) { |
| // Branch reuses inputs from its embedded Comparison. |
| // Instruction::WriteExtra is not called to avoid |
| // writing/reading inputs twice. |
| WriteExtraWithoutInputs(s); |
| comparison_->WriteExtra(s); |
| s->WriteRef<TargetEntryInstr*>(true_successor_); |
| s->WriteRef<TargetEntryInstr*>(false_successor_); |
| s->WriteRef<TargetEntryInstr*>(constant_target_); |
| } |
| |
| void BranchInstr::ReadExtra(FlowGraphDeserializer* d) { |
| ReadExtraWithoutInputs(d); |
| comparison_->ReadExtra(d); |
| for (intptr_t i = comparison_->InputCount() - 1; i >= 0; --i) { |
| comparison_->InputAt(i)->set_instruction(this); |
| } |
| true_successor_ = d->ReadRef<TargetEntryInstr*>(); |
| false_successor_ = d->ReadRef<TargetEntryInstr*>(); |
| constant_target_ = d->ReadRef<TargetEntryInstr*>(); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const compiler::ffi::CallbackMarshaller&>( |
| const compiler::ffi::CallbackMarshaller& x) { |
| UNIMPLEMENTED(); |
| } |
| |
| template <> |
| const compiler::ffi::CallbackMarshaller& |
| FlowGraphDeserializer::Read<const compiler::ffi::CallbackMarshaller&>() { |
| UNIMPLEMENTED(); |
| return *compiler::ffi::CallbackMarshaller::FromFunction( |
| Z, Function::null_function(), nullptr); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const compiler::ffi::CallMarshaller&>( |
| const compiler::ffi::CallMarshaller& x) { |
| Write<const Function&>(x.dart_signature()); |
| } |
| |
| template <> |
| const compiler::ffi::CallMarshaller& |
| FlowGraphDeserializer::Read<const compiler::ffi::CallMarshaller&>() { |
| const Function& dart_signature = Read<const Function&>(); |
| const char* error = nullptr; |
| return *compiler::ffi::CallMarshaller::FromFunction(Z, dart_signature, |
| &error); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const CallTargets&>(const CallTargets& x) { |
| x.Write(this); |
| } |
| |
| template <> |
| const CallTargets& FlowGraphDeserializer::Read<const CallTargets&>() { |
| return *(new (Z) CallTargets(this)); |
| } |
| |
| 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 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), |
| graph_entry_(d->graph_entry()), |
| 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::Write<const char*>(const char* x) { |
| ASSERT(x != nullptr); |
| const intptr_t len = strlen(x); |
| Write<intptr_t>(len); |
| stream_->WriteBytes(x, len); |
| } |
| |
| template <> |
| const char* FlowGraphDeserializer::Read<const char*>() { |
| const intptr_t len = Read<intptr_t>(); |
| char* str = zone()->Alloc<char>(len + 1); |
| stream_->ReadBytes(str, len); |
| str[len] = 0; |
| return str; |
| } |
| |
| void CheckConditionInstr::WriteExtra(FlowGraphSerializer* s) { |
| // CheckCondition reuses inputs from its embedded Comparison. |
| // Instruction::WriteExtra is not called to avoid |
| // writing/reading inputs twice. |
| WriteExtraWithoutInputs(s); |
| comparison_->WriteExtra(s); |
| } |
| |
| void CheckConditionInstr::ReadExtra(FlowGraphDeserializer* d) { |
| ReadExtraWithoutInputs(d); |
| comparison_->ReadExtra(d); |
| for (intptr_t i = comparison_->InputCount() - 1; i >= 0; --i) { |
| comparison_->InputAt(i)->set_instruction(this); |
| } |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<CidRangeValue>(CidRangeValue x) { |
| Write<intptr_t>(x.cid_start); |
| Write<intptr_t>(x.cid_end); |
| } |
| |
| template <> |
| CidRangeValue FlowGraphDeserializer::Read<CidRangeValue>() { |
| const intptr_t cid_start = Read<intptr_t>(); |
| const intptr_t cid_end = Read<intptr_t>(); |
| return CidRangeValue(cid_start, cid_end); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const Cids&>(const Cids& x) { |
| const intptr_t len = x.length(); |
| Write<intptr_t>(len); |
| for (intptr_t i = 0; i < len; ++i) { |
| const CidRange* r = x.At(i); |
| Write<intptr_t>(r->cid_start); |
| Write<intptr_t>(r->cid_end); |
| } |
| } |
| |
| template <> |
| const Cids& FlowGraphDeserializer::Read<const Cids&>() { |
| Cids* cids = new (Z) Cids(Z); |
| const intptr_t len = Read<intptr_t>(); |
| for (intptr_t i = 0; i < len; ++i) { |
| const intptr_t cid_start = Read<intptr_t>(); |
| const intptr_t cid_end = Read<intptr_t>(); |
| CidRange* r = new (Z) CidRange(cid_start, cid_end); |
| cids->Add(r); |
| } |
| return *cids; |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const Class&>(const Class& x) { |
| if (x.IsNull()) { |
| Write<classid_t>(kIllegalCid); |
| return; |
| } |
| Write<classid_t>(x.id()); |
| } |
| |
| template <> |
| const Class& FlowGraphDeserializer::Read<const Class&>() { |
| const classid_t cid = Read<classid_t>(); |
| if (cid == kIllegalCid) { |
| return Class::ZoneHandle(Z); |
| } |
| return Class::ZoneHandle(Z, 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::Write<const Code&>(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()) { |
| Write<intptr_t>(i); |
| return; |
| } |
| } |
| intptr_t index = StubCode::NumEntries(); |
| ObjectStore* object_store = isolate_group()->object_store(); |
| #define MATCH(member, name) \ |
| if (object_store->member() == x.ptr()) { \ |
| Write<intptr_t>(index); \ |
| return; \ |
| } \ |
| ++index; |
| OBJECT_STORE_STUB_CODE_LIST(MATCH) |
| #undef MATCH |
| UNIMPLEMENTED(); |
| } |
| |
| template <> |
| const Code& FlowGraphDeserializer::Read<const Code&>() { |
| const intptr_t stub_id = Read<intptr_t>(); |
| if (stub_id < StubCode::NumEntries()) { |
| return StubCode::EntryAt(stub_id); |
| } |
| intptr_t index = StubCode::NumEntries(); |
| ObjectStore* object_store = isolate_group()->object_store(); |
| #define MATCH(member, name) \ |
| if (index == stub_id) { \ |
| return Code::ZoneHandle(Z, object_store->member()); \ |
| } \ |
| ++index; |
| OBJECT_STORE_STUB_CODE_LIST(MATCH) |
| #undef MATCH |
| UNIMPLEMENTED(); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<CompileType*>(CompileType* x) { |
| if (x == nullptr) { |
| Write<bool>(false); |
| } else { |
| Write<bool>(true); |
| x->Write(this); |
| } |
| } |
| |
| template <> |
| CompileType* FlowGraphDeserializer::Read<CompileType*>() { |
| if (!Read<bool>()) { |
| return nullptr; |
| } |
| return new (Z) CompileType(this); |
| } |
| |
| 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::WriteRef<Definition*>(Definition* x) { |
| if (!x->HasSSATemp()) { |
| if (auto* push_arg = x->AsPushArgument()) { |
| // Environments of the calls can reference PushArgument instructions |
| // and they don't have SSA temps. |
| // Write a reference to the original definition. |
| // When reading it is restored using RepairPushArgsInEnvironment. |
| x = push_arg->value()->definition(); |
| } else { |
| UNREACHABLE(); |
| } |
| } |
| ASSERT(x->HasSSATemp()); |
| ASSERT(can_write_refs()); |
| Write<intptr_t>(x->ssa_temp_index()); |
| } |
| |
| template <> |
| Definition* FlowGraphDeserializer::ReadRef<Definition*>() { |
| return definition(Read<intptr_t>()); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<double>(double x) { |
| stream_->Write<int64_t>(bit_cast<int64_t>(x)); |
| } |
| |
| template <> |
| double FlowGraphDeserializer::Read<double>() { |
| return bit_cast<double>(stream_->Read<int64_t>()); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<Environment*>(Environment* x) { |
| ASSERT(can_write_refs()); |
| if (x == nullptr) { |
| Write<bool>(false); |
| } else { |
| Write<bool>(true); |
| x->Write(this); |
| } |
| } |
| |
| template <> |
| Environment* FlowGraphDeserializer::Read<Environment*>() { |
| if (!Read<bool>()) { |
| return nullptr; |
| } |
| return new (Z) Environment(this); |
| } |
| |
| 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<intptr_t>(flow_graph.inlining_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 (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
| Instruction* current = it.Current(); |
| 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 (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
| Instruction* current = it.Current(); |
| 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]); |
| } |
| |
| const auto* captured_parameters = flow_graph.captured_parameters(); |
| if (captured_parameters->IsEmpty()) { |
| Write<bool>(false); |
| } else { |
| Write<bool>(true); |
| // Captured parameters are rare so write their bit numbers |
| // instead of writing BitVector. |
| GrowableArray<intptr_t> indices(Z, 0); |
| for (intptr_t i = 0, n = captured_parameters->length(); i < n; ++i) { |
| if (captured_parameters->Contains(i)) { |
| indices.Add(i); |
| } |
| } |
| Write<GrowableArray<intptr_t>>(indices); |
| } |
| } |
| |
| FlowGraph* FlowGraphDeserializer::ReadFlowGraph() { |
| const intptr_t current_ssa_temp_index = Read<intptr_t>(); |
| const intptr_t max_block_id = Read<intptr_t>(); |
| const intptr_t inlining_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); |
| flow_graph->set_current_ssa_temp_index(current_ssa_temp_index); |
| flow_graph->CreateCommonConstants(); |
| flow_graph->disallow_licm(); |
| flow_graph->set_inlining_id(inlining_id); |
| 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(true); |
| ASSERT(codegen_block_order == &flow_graph->optimized_block_order()); |
| for (intptr_t i = 0; i < num_blocks; ++i) { |
| codegen_block_order->Add(ReadRef<BlockEntryInstr*>()); |
| } |
| } |
| } |
| |
| if (Read<bool>()) { |
| GrowableArray<intptr_t> indices = Read<GrowableArray<intptr_t>>(); |
| for (intptr_t i : indices) { |
| flow_graph->captured_parameters()->Add(i); |
| } |
| } |
| |
| return flow_graph; |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const Function&>(const Function& x) { |
| if (x.IsNull()) { |
| Write<int8_t>(-1); |
| return; |
| } |
| 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(Z, x.Owner()); |
| Write<classid_t>(owner.id()); |
| const intptr_t function_index = owner.FindFunctionIndex(x); |
| ASSERT(function_index >= 0); |
| Write<intptr_t>(function_index); |
| return; |
| } |
| case UntaggedFunction::kImplicitClosureFunction: { |
| const auto& parent = Function::Handle(Z, x.parent_function()); |
| Write<const Function&>(parent); |
| return; |
| } |
| case UntaggedFunction::kFieldInitializer: { |
| const auto& field = Field::Handle(Z, x.accessor_field()); |
| 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. |
| Write<intptr_t>(ClosureFunctionsCache::FindClosureIndex(x)); |
| return; |
| case UntaggedFunction::kMethodExtractor: { |
| Function& function = Function::Handle(Z, x.extracted_method_closure()); |
| ASSERT(function.IsImplicitClosureFunction()); |
| function = function.parent_function(); |
| Write<const Function&>(function); |
| Write<const String&>(String::Handle(Z, x.name())); |
| return; |
| } |
| case UntaggedFunction::kInvokeFieldDispatcher: { |
| Write<const Class&>(Class::Handle(Z, x.Owner())); |
| Write<const String&>(String::Handle(Z, x.name())); |
| Write<const Array&>(Array::Handle(Z, x.saved_args_desc())); |
| return; |
| } |
| case UntaggedFunction::kDynamicInvocationForwarder: { |
| const auto& target = Function::Handle(Z, x.ForwardingTarget()); |
| Write<const Function&>(target); |
| return; |
| } |
| case UntaggedFunction::kFfiTrampoline: { |
| if (x.FfiCallbackTarget() != Object::null()) { |
| UNIMPLEMENTED(); |
| } |
| Write<const String&>(String::Handle(Z, x.name())); |
| Write<const FunctionType&>(FunctionType::Handle(Z, x.signature())); |
| Write<const FunctionType&>(FunctionType::Handle(Z, x.FfiCSignature())); |
| Write<bool>(x.FfiIsLeaf()); |
| return; |
| } |
| default: |
| break; |
| } |
| switch (x.kind()) { |
| #define UNIMPLEMENTED_FUNCTION_KIND(kind) \ |
| case UntaggedFunction::k##kind: \ |
| FATAL("Unimplemented Write<const Function&> for " #kind); |
| FOR_EACH_RAW_FUNCTION_KIND(UNIMPLEMENTED_FUNCTION_KIND) |
| #undef UNIMPLEMENTED_FUNCTION_KIND |
| } |
| UNREACHABLE(); |
| } |
| |
| template <> |
| const Function& FlowGraphDeserializer::Read<const Function&>() { |
| const int8_t raw_kind = Read<int8_t>(); |
| if (raw_kind < 0) { |
| return Object::null_function(); |
| } |
| 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 = Read<classid_t>(); |
| const intptr_t function_index = Read<intptr_t>(); |
| const auto& owner = Class::Handle(Z, GetClassById(owner_class_id)); |
| const auto& result = |
| Function::ZoneHandle(Z, owner.FunctionFromIndex(function_index)); |
| ASSERT(!result.IsNull()); |
| return result; |
| } |
| case UntaggedFunction::kImplicitClosureFunction: { |
| const auto& parent = Read<const Function&>(); |
| return Function::ZoneHandle(Z, parent.ImplicitClosureFunction()); |
| } |
| case UntaggedFunction::kFieldInitializer: { |
| const auto& field = Read<const Field&>(); |
| return Function::ZoneHandle(Z, field.EnsureInitializerFunction()); |
| } |
| case UntaggedFunction::kClosureFunction: { |
| const intptr_t index = Read<intptr_t>(); |
| return Function::ZoneHandle( |
| Z, ClosureFunctionsCache::ClosureFunctionFromIndex(index)); |
| } |
| case UntaggedFunction::kMethodExtractor: { |
| const Function& function = Read<const Function&>(); |
| const String& name = Read<const String&>(); |
| return Function::ZoneHandle(Z, function.GetMethodExtractor(name)); |
| } |
| case UntaggedFunction::kInvokeFieldDispatcher: { |
| const Class& owner = Read<const Class&>(); |
| const String& target_name = Read<const String&>(); |
| const Array& args_desc = Read<const Array&>(); |
| return Function::ZoneHandle( |
| Z, |
| owner.GetInvocationDispatcher( |
| target_name, args_desc, UntaggedFunction::kInvokeFieldDispatcher, |
| /*create_if_absent=*/true)); |
| } |
| case UntaggedFunction::kDynamicInvocationForwarder: { |
| const auto& target = Read<const Function&>(); |
| auto& name = String::Handle(Z, target.name()); |
| name = Function::CreateDynamicInvocationForwarderName(name); |
| return Function::ZoneHandle(Z, |
| target.GetDynamicInvocationForwarder(name)); |
| } |
| case UntaggedFunction::kFfiTrampoline: { |
| const String& name = Read<const String&>(); |
| const FunctionType& signature = Read<const FunctionType&>(); |
| const FunctionType& c_signature = Read<const FunctionType&>(); |
| const bool is_leaf = Read<bool>(); |
| return Function::ZoneHandle( |
| Z, compiler::ffi::TrampolineFunction(name, signature, c_signature, |
| is_leaf)); |
| } |
| 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<CatchBlockEntryInstr*>(catch_entries_); |
| 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*>(); |
| catch_entries_ = d->ReadGrowableArrayOfRefs<CatchBlockEntryInstr*>(); |
| 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::Write<const ICData*>(const ICData* x) { |
| if (x == nullptr) { |
| Write<bool>(false); |
| } else { |
| Write<bool>(true); |
| ASSERT(!x->IsNull()); |
| Write<const Object&>(*x); |
| } |
| } |
| |
| template <> |
| const ICData* FlowGraphDeserializer::Read<const ICData*>() { |
| if (!Read<bool>()) { |
| return nullptr; |
| } |
| return &ICData::Cast(Read<const Object&>()); |
| } |
| |
| void IfThenElseInstr::WriteExtra(FlowGraphSerializer* s) { |
| // IfThenElse reuses inputs from its embedded Comparison. |
| // Definition::WriteExtra is not called to avoid |
| // writing/reading inputs twice. |
| WriteExtraWithoutInputs(s); |
| comparison_->WriteExtra(s); |
| } |
| |
| void IfThenElseInstr::ReadExtra(FlowGraphDeserializer* d) { |
| ReadExtraWithoutInputs(d); |
| comparison_->ReadExtra(d); |
| for (intptr_t i = comparison_->InputCount() - 1; i >= 0; --i) { |
| comparison_->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::Write<Instruction*>(Instruction* x) { |
| if (x == nullptr) { |
| Write<uint8_t>(Instruction::kNumInstructions); |
| } else { |
| Write<uint8_t>(static_cast<uint8_t>(x->tag())); |
| x->WriteTo(this); |
| } |
| } |
| |
| template <> |
| Instruction* FlowGraphDeserializer::Read<Instruction*>() { |
| const uint8_t tag = Read<uint8_t>(); |
| switch (tag) { |
| #define READ_INSTRUCTION(type, attrs) \ |
| case Instruction::k##type: \ |
| return new (Z) type##Instr(this); |
| FOR_EACH_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(Comparison, ComparisonInstr) \ |
| V(Constant, ConstantInstr) \ |
| V(Definition, Definition) \ |
| V(ParallelMove, ParallelMoveInstr) \ |
| V(Phi, PhiInstr) |
| |
| #define SERIALIZABLE_AS_INSTRUCTION(name, type) \ |
| template <> \ |
| void FlowGraphSerializer::Write<type*>(type * x) { \ |
| Write<Instruction*>(x); \ |
| } \ |
| template <> \ |
| type* FlowGraphDeserializer::Read<type*>() { \ |
| Instruction* instr = 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::Write<int8_t>(int8_t x) { |
| stream_->Write<int8_t>(x); |
| } |
| |
| template <> |
| int8_t FlowGraphDeserializer::Read<int8_t>() { |
| return stream_->Read<int8_t>(); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<int16_t>(int16_t x) { |
| stream_->Write<int16_t>(x); |
| } |
| |
| template <> |
| int16_t FlowGraphDeserializer::Read<int16_t>() { |
| return stream_->Read<int16_t>(); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<int32_t>(int32_t x) { |
| stream_->Write<int32_t>(x); |
| } |
| |
| template <> |
| int32_t FlowGraphDeserializer::Read<int32_t>() { |
| return stream_->Read<int32_t>(); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<int64_t>(int64_t x) { |
| stream_->Write<int64_t>(x); |
| } |
| |
| template <> |
| int64_t FlowGraphDeserializer::Read<int64_t>() { |
| return 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::Write<const LocalVariable&>(const LocalVariable& x) { |
| UNIMPLEMENTED(); |
| } |
| |
| template <> |
| const LocalVariable& FlowGraphDeserializer::Read<const LocalVariable&>() { |
| UNIMPLEMENTED(); |
| return *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) { |
| return Location::Pair(Location::Read(d), Location::Read(d)); |
| } 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::Write<LocationSummary*>(LocationSummary* x) { |
| ASSERT(can_write_refs()); |
| if (x == nullptr) { |
| Write<bool>(false); |
| } else { |
| Write<bool>(true); |
| x->Write(this); |
| } |
| } |
| |
| template <> |
| LocationSummary* FlowGraphDeserializer::Read<LocationSummary*>() { |
| if (!Read<bool>()) { |
| return nullptr; |
| } |
| return new (Z) LocationSummary(this); |
| } |
| |
| 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::Write<MoveOperands*>(MoveOperands* x) { |
| ASSERT(x != nullptr); |
| x->src().Write(this); |
| x->dest().Write(this); |
| } |
| |
| template <> |
| MoveOperands* FlowGraphDeserializer::Read<MoveOperands*>() { |
| Location src = Location::Read(this); |
| Location dest = Location::Read(this); |
| return new (Z) MoveOperands(dest, src); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const compiler::ffi::NativeCallingConvention&>( |
| const compiler::ffi::NativeCallingConvention& x) { |
| // A subset of NativeCallingConvention currently used by CCallInstr. |
| const auto& args = x.argument_locations(); |
| for (intptr_t i = 0, n = args.length(); i < n; ++i) { |
| if (args.At(i)->payload_type().AsRepresentation() != kUnboxedFfiIntPtr) { |
| UNIMPLEMENTED(); |
| } |
| } |
| if (x.return_location().payload_type().AsRepresentation() != |
| kUnboxedFfiIntPtr) { |
| UNIMPLEMENTED(); |
| } |
| Write<intptr_t>(args.length()); |
| } |
| |
| template <> |
| const compiler::ffi::NativeCallingConvention& |
| FlowGraphDeserializer::Read<const compiler::ffi::NativeCallingConvention&>() { |
| const intptr_t num_args = Read<intptr_t>(); |
| const auto& native_function_type = |
| *compiler::ffi::NativeFunctionType::FromUnboxedRepresentation( |
| Z, num_args, kUnboxedFfiIntPtr); |
| return compiler::ffi::NativeCallingConvention::FromSignature( |
| Z, native_function_type); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const Object&>(const Object& x) { |
| const intptr_t cid = x.GetClassId(); |
| ASSERT(cid != kIllegalCid); |
| // Do not write objects repeatedly. |
| const intptr_t object_id = heap_->GetObjectId(x.ptr()); |
| if (object_id != 0) { |
| const intptr_t object_index = object_id - 1; |
| Write<intptr_t>(kIllegalCid); |
| Write<intptr_t>(object_index); |
| return; |
| } |
| const intptr_t object_index = object_counter_++; |
| heap_->SetObjectId(x.ptr(), object_index + 1); |
| Write<intptr_t>(cid); |
| WriteObjectImpl(x, cid, object_index); |
| } |
| |
| template <> |
| const Object& FlowGraphDeserializer::Read<const Object&>() { |
| const intptr_t cid = Read<intptr_t>(); |
| if (cid == kIllegalCid) { |
| const intptr_t object_index = Read<intptr_t>(); |
| return *objects_[object_index]; |
| } |
| const intptr_t object_index = object_counter_; |
| object_counter_++; |
| const Object& result = ReadObjectImpl(cid, object_index); |
| 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; |
| } |
| |
| 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.context() != 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 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()); |
| TypeScope type_scope(this, type.IsRecursive()); |
| 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())); |
| AbstractType& t = AbstractType::Handle(Z, type.result_type()); |
| Write<const AbstractType&>(t); |
| // Do not write parameter types as Array to avoid eager canonicalization |
| // when reading. |
| const Array& param_types = Array::Handle(Z, type.parameter_types()); |
| ASSERT(param_types.Length() == type.NumParameters()); |
| for (intptr_t i = 0, n = type.NumParameters(); i < n; ++i) { |
| t ^= param_types.At(i); |
| Write<const AbstractType&>(t); |
| } |
| Write<const Array&>(Array::Handle(Z, type.named_parameter_names())); |
| Write<bool>(type_scope.CanBeCanonicalized()); |
| 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())); |
| Write<const Array&>(Array::Handle(Z, icdata.arguments_descriptor())); |
| 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 kImmutableLinkedHashMapCid: |
| case kImmutableLinkedHashSetCid: { |
| 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 == kImmutableLinkedHashMapCid) { |
| 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).AsInt64Value()); |
| 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 kSentinelCid: |
| if (x.ptr() == Object::sentinel().ptr()) { |
| Write<bool>(true); |
| } else if (x.ptr() == Object::transition_sentinel().ptr()) { |
| Write<bool>(false); |
| } 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()); |
| const auto& cls = Class::Handle(Z, type.type_class()); |
| TypeScope type_scope(this, type.IsRecursive() && cls.IsGeneric()); |
| 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); |
| } |
| Write<bool>(type_scope.CanBeCanonicalized()); |
| break; |
| } |
| case kTypeArgumentsCid: { |
| const auto& type_args = TypeArguments::Cast(x); |
| ASSERT(type_args.IsFinalized()); |
| TypeScope type_scope(this, type_args.IsRecursive()); |
| 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); |
| } |
| Write<bool>(type_scope.CanBeCanonicalized()); |
| break; |
| } |
| case kTypeParameterCid: { |
| const auto& tp = TypeParameter::Cast(x); |
| ASSERT(tp.IsFinalized()); |
| TypeScope type_scope(this, tp.IsRecursive()); |
| Write<classid_t>(tp.parameterized_class_id()); |
| Write<intptr_t>(tp.base()); |
| Write<intptr_t>(tp.index()); |
| Write<int8_t>(static_cast<int8_t>(tp.nullability())); |
| Write<const AbstractType&>(AbstractType::Handle(Z, tp.bound())); |
| Write<bool>(type_scope.CanBeCanonicalized()); |
| 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; |
| } |
| case kTypeRefCid: { |
| const auto& tr = TypeRef::Cast(x); |
| ASSERT(tr.IsFinalized()); |
| TypeScope type_scope(this, tr.IsRecursive()); |
| Write<const AbstractType&>(AbstractType::Handle(Z, tr.type())); |
| Write<bool>(type_scope.CanBeCanonicalized()); |
| 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()->shared_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, Context::Handle(Z))); |
| closure ^= closure.Canonicalize(thread()); |
| return closure; |
| } |
| case kDoubleCid: |
| return Double::ZoneHandle(Z, Double::NewCanonical(Read<double>())); |
| 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 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&>()); |
| const Array& param_types = |
| Array::Handle(Z, Array::New(result.NumParameters(), Heap::kOld)); |
| for (intptr_t i = 0, n = result.NumParameters(); i < n; ++i) { |
| param_types.SetAt(i, Read<const AbstractType&>()); |
| } |
| result.set_parameter_types(param_types); |
| result.set_named_parameter_names(Read<const Array&>()); |
| result.SetIsFinalized(); |
| result ^= MaybeCanonicalize(result, object_index, Read<bool>()); |
| 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 = Read<const Array&>(); |
| 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 kImmutableLinkedHashMapCid: |
| case kImmutableLinkedHashSetCid: { |
| 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 == kImmutableLinkedHashMapCid) { |
| result = ImmutableLinkedHashMap::NewUninitialized(Heap::kOld); |
| used_data = (length << 1); |
| } else { |
| result = ImmutableLinkedHashSet::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 kSentinelCid: |
| return Read<bool>() ? Object::sentinel() : Object::transition_sentinel(); |
| 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 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 ^= MaybeCanonicalize(result, object_index, Read<bool>()); |
| return result; |
| } |
| case kTypeArgumentsCid: { |
| 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 ^= MaybeCanonicalize(type_args, object_index, Read<bool>()); |
| return type_args; |
| } |
| case kTypeParameterCid: { |
| const classid_t parameterized_class_id = Read<classid_t>(); |
| const intptr_t base = Read<intptr_t>(); |
| const intptr_t index = Read<intptr_t>(); |
| const Nullability nullability = static_cast<Nullability>(Read<int8_t>()); |
| const auto& parameterized_class = |
| Class::Handle(Z, (parameterized_class_id == kFunctionCid) |
| ? Class::null() |
| : GetClassById(parameterized_class_id)); |
| auto& tp = TypeParameter::ZoneHandle( |
| Z, TypeParameter::New(parameterized_class, base, index, |
| /*bound=*/Object::null_abstract_type(), |
| nullability)); |
| SetObjectAt(object_index, tp); |
| const auto& bound = Read<const AbstractType&>(); |
| tp.set_bound(bound); |
| tp.SetIsFinalized(); |
| tp ^= MaybeCanonicalize(tp, object_index, Read<bool>()); |
| 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; |
| } |
| case kTypeRefCid: { |
| auto& tr = |
| TypeRef::ZoneHandle(Z, TypeRef::New(Object::null_abstract_type())); |
| SetObjectAt(object_index, tr); |
| const auto& type = Read<const AbstractType&>(); |
| ASSERT(!type.IsNull()); |
| tr.set_type(type); |
| tr ^= MaybeCanonicalize(tr, object_index, Read<bool>()); |
| return tr; |
| } |
| default: |
| if ((cid >= kNumPredefinedCids) || (cid == kInstanceCid)) { |
| const auto& cls = Class::Handle(Z, GetClassById(cid)); |
| const auto unboxed_fields_bitmap = |
| isolate_group()->shared_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(); |
| } |
| |
| InstancePtr FlowGraphDeserializer::MaybeCanonicalize( |
| const Instance& obj, |
| intptr_t object_index, |
| bool can_be_canonicalized) { |
| if (can_be_canonicalized) { |
| intptr_t remaining = 0; |
| for (intptr_t idx : pending_canonicalization_) { |
| if (idx < object_index) { |
| pending_canonicalization_[remaining++] = idx; |
| } else { |
| objects_[idx] = &Instance::ZoneHandle( |
| Z, Instance::Cast(*objects_[idx]).Canonicalize(thread())); |
| } |
| } |
| pending_canonicalization_.TruncateTo(remaining); |
| return obj.Canonicalize(thread()); |
| } else { |
| ASSERT(objects_[object_index]->ptr() == obj.ptr()); |
| pending_canonicalization_.Add(object_index); |
| return obj.ptr(); |
| } |
| } |
| |
| #define HANDLES_SERIALIZABLE_AS_OBJECT(V) \ |
| V(AbstractType, Object::null_abstract_type()) \ |
| V(Array, Object::null_array()) \ |
| V(Field, Field::Handle(Z)) \ |
| V(FunctionType, Object::null_function_type()) \ |
| V(String, Object::null_string()) \ |
| V(TypeArguments, Object::null_type_arguments()) \ |
| V(TypeParameters, TypeParameters::Handle(Z)) |
| |
| #define SERIALIZE_HANDLE_AS_OBJECT(handle, null_handle) \ |
| template <> \ |
| void FlowGraphSerializer::Write<const handle&>(const handle& x) { \ |
| Write<const Object&>(x); \ |
| } \ |
| template <> \ |
| const handle& FlowGraphDeserializer::Read<const handle&>() { \ |
| const Object& result = 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_); |
| } |
| |
| void ParallelMoveInstr::ReadExtra(FlowGraphDeserializer* d) { |
| Instruction::ReadExtra(d); |
| moves_ = d->Read<GrowableArray<MoveOperands*>>(); |
| } |
| |
| 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>()) {} |
| |
| template <> |
| void FlowGraphSerializer::Write<Range*>(Range* x) { |
| if (x == nullptr) { |
| Write<bool>(false); |
| } else { |
| Write<bool>(true); |
| x->Write(this); |
| } |
| } |
| |
| template <> |
| Range* FlowGraphDeserializer::Read<Range*>() { |
| if (!Read<bool>()) { |
| return nullptr; |
| } |
| return new (Z) Range(this); |
| } |
| |
| void Range::Write(FlowGraphSerializer* s) const { |
| min_.Write(s); |
| max_.Write(s); |
| } |
| |
| Range::Range(FlowGraphDeserializer* d) |
| : min_(RangeBoundary(d)), max_(RangeBoundary(d)) {} |
| |
| void RangeBoundary::Write(FlowGraphSerializer* s) const { |
| 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>()) {} |
| |
| 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::Write<Representation>(Representation x) { |
| Write<uint8_t>(x); |
| } |
| |
| template <> |
| Representation FlowGraphDeserializer::Read<Representation>() { |
| return static_cast<Representation>(Read<uint8_t>()); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const Slot&>(const Slot& x) { |
| x.Write(this); |
| } |
| |
| template <> |
| const Slot& FlowGraphDeserializer::Read<const Slot&>() { |
| return Slot::Read(this); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const Slot*>(const Slot* x) { |
| if (x == nullptr) { |
| Write<bool>(false); |
| return; |
| } |
| Write<bool>(true); |
| x->Write(this); |
| } |
| |
| template <> |
| const Slot* FlowGraphDeserializer::Read<const Slot*>() { |
| if (!Read<bool>()) { |
| return nullptr; |
| } |
| return &Slot::Read(this); |
| } |
| |
| 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::kCapturedVariable: |
| s->Write<int8_t>(flags_); |
| s->Write<intptr_t>(offset_in_bytes_); |
| s->Write<const String&>(*DataAs<const String>()); |
| s->Write<const AbstractType&>(*static_type_); |
| 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; |
| ClassIdTagType cid = kDynamicCid; |
| intptr_t offset = -1; |
| const void* data = nullptr; |
| const AbstractType* static_type = nullptr; |
| Representation representation = kTagged; |
| |
| switch (kind) { |
| case Kind::kTypeArguments: |
| flags = d->Read<int8_t>(); |
| offset = d->Read<intptr_t>(); |
| cid = kTypeArgumentsCid; |
| data = ":type_arguments"; |
| break; |
| case Kind::kTypeArgumentsIndex: |
| flags = |
| IsImmutableBit::encode(true) | |
| IsCompressedBit::encode(TypeArguments::ContainsCompressedPointers()); |
| offset = d->Read<intptr_t>(); |
| data = ":argument"; |
| break; |
| case Kind::kArrayElement: |
| flags = IsNullableBit::encode(true) | |
| IsCompressedBit::encode(Array::ContainsCompressedPointers()); |
| offset = d->Read<intptr_t>(); |
| data = ":array_element"; |
| break; |
| case Kind::kCapturedVariable: |
| flags = d->Read<int8_t>(); |
| offset = d->Read<intptr_t>(); |
| data = &d->Read<const String&>(); |
| static_type = &d->Read<const AbstractType&>(); |
| 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, cid, offset, data, |
| static_type, representation); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<const compiler::TableSelector*>( |
| const compiler::TableSelector* x) { |
| #if defined(DART_PRECOMPILER) |
| ASSERT(x != nullptr); |
| Write<int32_t>(x->id); |
| #else |
| UNREACHABLE(); |
| #endif |
| } |
| |
| template <> |
| const compiler::TableSelector* |
| FlowGraphDeserializer::Read<const compiler::TableSelector*>() { |
| #if defined(DART_PRECOMPILER) |
| const int32_t id = Read<int32_t>(); |
| const compiler::TableSelector* selector = |
| Precompiler::Instance()->selector_map()->GetSelector(id); |
| ASSERT(selector != nullptr); |
| return selector; |
| #else |
| UNREACHABLE(); |
| #endif |
| } |
| |
| void SpecialParameterInstr::WriteExtra(FlowGraphSerializer* s) { |
| TemplateDefinition::WriteExtra(s); |
| s->WriteRef<BlockEntryInstr*>(block_); |
| } |
| |
| void SpecialParameterInstr::ReadExtra(FlowGraphDeserializer* d) { |
| TemplateDefinition::ReadExtra(d); |
| block_ = d->ReadRef<BlockEntryInstr*>(); |
| } |
| |
| template <intptr_t kExtraInputs> |
| void TemplateDartCall<kExtraInputs>::WriteExtra(FlowGraphSerializer* s) { |
| VariadicDefinition::WriteExtra(s); |
| if (push_arguments_ == nullptr) { |
| s->Write<intptr_t>(-1); |
| } else { |
| s->Write<intptr_t>(push_arguments_->length()); |
| #if defined(DEBUG) |
| // Verify that PushArgument instructions are inserted immediately |
| // before this instruction. ReadExtra below relies on |
| // that when restoring push_arguments_. |
| Instruction* instr = this; |
| for (intptr_t i = push_arguments_->length() - 1; i >= 0; --i) { |
| do { |
| instr = instr->previous(); |
| ASSERT(instr != nullptr); |
| } while (!instr->IsPushArgument()); |
| ASSERT(instr == (*push_arguments_)[i]); |
| } |
| #endif |
| } |
| } |
| |
| template <intptr_t kExtraInputs> |
| void TemplateDartCall<kExtraInputs>::ReadExtra(FlowGraphDeserializer* d) { |
| VariadicDefinition::ReadExtra(d); |
| const intptr_t num_push_args = d->Read<intptr_t>(); |
| if (num_push_args >= 0) { |
| push_arguments_ = |
| new (d->zone()) PushArgumentsArray(d->zone(), num_push_args); |
| push_arguments_->EnsureLength(num_push_args, nullptr); |
| Instruction* instr = this; |
| for (int i = num_push_args - 1; i >= 0; --i) { |
| do { |
| instr = instr->previous(); |
| ASSERT(instr != nullptr); |
| } while (!instr->IsPushArgument()); |
| (*push_arguments_)[i] = instr->AsPushArgument(); |
| } |
| if (env() != nullptr) { |
| RepairPushArgsInEnvironment(); |
| } |
| } |
| } |
| |
| // Explicit template instantiations, needed for the methods above. |
| template class TemplateDartCall<0>; |
| template class TemplateDartCall<1>; |
| |
| template <> |
| void FlowGraphSerializer::Write<TokenPosition>(TokenPosition x) { |
| Write<int32_t>(x.Serialize()); |
| } |
| |
| template <> |
| TokenPosition FlowGraphDeserializer::Read<TokenPosition>() { |
| return TokenPosition::Deserialize(Read<int32_t>()); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<uint8_t>(uint8_t x) { |
| stream_->Write<uint8_t>(x); |
| } |
| |
| template <> |
| uint8_t FlowGraphDeserializer::Read<uint8_t>() { |
| return stream_->Read<uint8_t>(); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<uint16_t>(uint16_t x) { |
| stream_->Write<uint16_t>(x); |
| } |
| |
| template <> |
| uint16_t FlowGraphDeserializer::Read<uint16_t>() { |
| return stream_->Read<uint16_t>(); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<uint32_t>(uint32_t x) { |
| stream_->Write<int32_t>(static_cast<int32_t>(x)); |
| } |
| |
| template <> |
| uint32_t FlowGraphDeserializer::Read<uint32_t>() { |
| return static_cast<uint32_t>(stream_->Read<int32_t>()); |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<uint64_t>(uint64_t x) { |
| stream_->Write<int64_t>(static_cast<int64_t>(x)); |
| } |
| |
| template <> |
| uint64_t FlowGraphDeserializer::Read<uint64_t>() { |
| return static_cast<uint64_t>(stream_->Read<int64_t>()); |
| } |
| |
| void UnboxedConstantInstr::WriteTo(FlowGraphSerializer* s) { |
| ConstantInstr::WriteTo(s); |
| s->Write<Representation>(representation_); |
| // constant_address_ is not written - it is restored when reading. |
| } |
| |
| UnboxedConstantInstr::UnboxedConstantInstr(FlowGraphDeserializer* d) |
| : ConstantInstr(d), |
| representation_(d->Read<Representation>()), |
| constant_address_(0) { |
| if (representation_ == kUnboxedDouble) { |
| ASSERT(value().IsDouble()); |
| constant_address_ = FindDoubleConstant(Double::Cast(value()).value()); |
| } |
| } |
| |
| template <> |
| void FlowGraphSerializer::Write<Value*>(Value* x) { |
| ASSERT(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; |
| } |
| Write<CompileType*>(reaching_type); |
| WriteRef<Definition*>(def); |
| } |
| |
| template <> |
| Value* FlowGraphDeserializer::Read<Value*>() { |
| CompileType* type = Read<CompileType*>(); |
| Definition* def = ReadRef<Definition*>(); |
| Value* value = new (Z) 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 |