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