// Copyright (c) 2017, 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/compilation_trace.h" #include "vm/compiler/jit/compiler.h" #include "vm/globals.h" #include "vm/log.h" #include "vm/longjump.h" #include "vm/object_store.h" #include "vm/resolver.h" #include "vm/symbols.h" #include "vm/version.h" namespace dart { #if !defined(DART_PRECOMPILED_RUNTIME) DEFINE_FLAG(bool, trace_compilation_trace, false, "Trace compilation trace."); CompilationTraceSaver::CompilationTraceSaver(Zone* zone) : buf_(zone, 1 * MB), func_name_(String::Handle(zone)), cls_(Class::Handle(zone)), cls_name_(String::Handle(zone)), lib_(Library::Handle(zone)), uri_(String::Handle(zone)) {} void CompilationTraceSaver::Visit(const Function& function) { if (!function.HasCode()) { return; // Not compiled. } if (function.parent_function() != Function::null()) { // Lookup works poorly for local functions. We compile all local functions // in a compiled function instead. return; } func_name_ = function.name(); func_name_ = String::RemovePrivateKey(func_name_); cls_ = function.Owner(); cls_name_ = cls_.Name(); cls_name_ = String::RemovePrivateKey(cls_name_); lib_ = cls_.library(); uri_ = lib_.url(); buf_.Printf("%s,%s,%s\n", uri_.ToCString(), cls_name_.ToCString(), func_name_.ToCString()); } CompilationTraceLoader::CompilationTraceLoader(Thread* thread) : thread_(thread), zone_(thread->zone()), uri_(String::Handle(zone_)), class_name_(String::Handle(zone_)), function_name_(String::Handle(zone_)), function_name2_(String::Handle(zone_)), lib_(Library::Handle(zone_)), cls_(Class::Handle(zone_)), function_(Function::Handle(zone_)), function2_(Function::Handle(zone_)), field_(Field::Handle(zone_)), sites_(Array::Handle(zone_)), site_(ICData::Handle(zone_)), static_type_(AbstractType::Handle(zone_)), receiver_cls_(Class::Handle(zone_)), target_(Function::Handle(zone_)), selector_(String::Handle(zone_)), args_desc_(Array::Handle(zone_)), error_(Object::Handle(zone_)) {} static char* FindCharacter(char* str, char goal, char* limit) { while (str < limit) { if (*str == goal) { return str; } str++; } return NULL; } RawObject* CompilationTraceLoader::CompileTrace(uint8_t* buffer, intptr_t size) { // First compile functions named in the trace. char* cursor = reinterpret_cast(buffer); char* limit = cursor + size; while (cursor < limit) { char* uri = cursor; char* comma1 = FindCharacter(uri, ',', limit); if (comma1 == NULL) { break; } *comma1 = 0; char* cls_name = comma1 + 1; char* comma2 = FindCharacter(cls_name, ',', limit); if (comma2 == NULL) { break; } *comma2 = 0; char* func_name = comma2 + 1; char* newline = FindCharacter(func_name, '\n', limit); if (newline == NULL) { break; } *newline = 0; error_ = CompileTriple(uri, cls_name, func_name); if (error_.IsError()) { return error_.raw(); } cursor = newline + 1; } // Next, compile common dispatchers. These aren't found with the normal // lookup above because they have irregular lookup that depends on the // arguments descriptor (e.g. call() versus call(x)). const Class& closure_class = Class::Handle(zone_, thread_->isolate()->object_store()->closure_class()); Array& arguments_descriptor = Array::Handle(zone_); Function& dispatcher = Function::Handle(zone_); for (intptr_t argc = 1; argc <= 4; argc++) { const intptr_t kTypeArgsLen = 0; arguments_descriptor = ArgumentsDescriptor::New(kTypeArgsLen, argc); dispatcher = closure_class.GetInvocationDispatcher( Symbols::Call(), arguments_descriptor, RawFunction::kInvokeFieldDispatcher, true /* create_if_absent */); error_ = CompileFunction(dispatcher); if (error_.IsError()) { return error_.raw(); } } // Finally, compile closures in all compiled functions. Don't cache the // length since compiling may append to this list. const GrowableObjectArray& closure_functions = GrowableObjectArray::Handle( zone_, thread_->isolate()->object_store()->closure_functions()); for (intptr_t i = 0; i < closure_functions.Length(); i++) { function_ ^= closure_functions.At(i); function2_ = function_.parent_function(); if (function2_.HasCode()) { error_ = CompileFunction(function_); if (error_.IsError()) { return error_.raw(); } } } return Object::null(); } // Use a fuzzy match to find the right function to compile. This allows a // compilation trace to remain mostly valid in the face of program changes, and // deals with implicit/dispatcher functions that don't have proper names. // - Ignore private name mangling // - If looking for a getter and we only have the corresponding regular method, // compile the regular method, create its implicit closure and compile that. // - If looking for a regular method and we only have the corresponding getter, // compile the getter, create its method extractor and compile that. // - If looking for a getter and we only have a const field, evaluate the const // field. RawObject* CompilationTraceLoader::CompileTriple(const char* uri_cstr, const char* cls_cstr, const char* func_cstr) { uri_ = Symbols::New(thread_, uri_cstr); class_name_ = Symbols::New(thread_, cls_cstr); function_name_ = Symbols::New(thread_, func_cstr); if (function_name_.Equals("_getMainClosure")) { // The scheme for invoking main relies on compiling _getMainClosure after // synthetically importing the root library. if (FLAG_trace_compilation_trace) { THR_Print("Compilation trace: skip %s,%s,%s\n", uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString()); } return Object::null(); } lib_ = Library::LookupLibrary(thread_, uri_); if (lib_.IsNull()) { // Missing library. if (FLAG_trace_compilation_trace) { THR_Print("Compilation trace: missing library %s,%s,%s\n", uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString()); } return Object::null(); } bool is_dyn = Function::IsDynamicInvocationForwarderName(function_name_); if (is_dyn) { function_name_ = Function::DemangleDynamicInvocationForwarderName(function_name_); } bool is_getter = Field::IsGetterName(function_name_); bool is_init = Field::IsInitName(function_name_); bool add_closure = false; bool processed = false; if (class_name_.Equals(Symbols::TopLevel())) { function_ = lib_.LookupFunctionAllowPrivate(function_name_); field_ = lib_.LookupFieldAllowPrivate(function_name_); if (function_.IsNull() && is_getter) { // Maybe this was a tear off. add_closure = true; function_name2_ = Field::NameFromGetter(function_name_); function_ = lib_.LookupFunctionAllowPrivate(function_name2_); field_ = lib_.LookupFieldAllowPrivate(function_name2_); } if (field_.IsNull() && is_getter) { function_name2_ = Field::NameFromGetter(function_name_); field_ = lib_.LookupFieldAllowPrivate(function_name2_); } if (field_.IsNull() && is_init) { function_name2_ = Field::NameFromInit(function_name_); field_ = lib_.LookupFieldAllowPrivate(function_name2_); } } else { cls_ = lib_.SlowLookupClassAllowMultiPartPrivate(class_name_); if (cls_.IsNull()) { // Missing class. if (FLAG_trace_compilation_trace) { THR_Print("Compilation trace: missing class %s,%s,%s\n", uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString()); } return Object::null(); } error_ = cls_.EnsureIsFinalized(thread_); if (error_.IsError()) { // Non-finalized class. if (FLAG_trace_compilation_trace) { THR_Print("Compilation trace: non-finalized class %s,%s,%s (%s)\n", uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); } return error_.raw(); } function_ = cls_.LookupFunctionAllowPrivate(function_name_); field_ = cls_.LookupFieldAllowPrivate(function_name_); if (function_.IsNull() && is_getter) { // Maybe this was a tear off. add_closure = true; function_name2_ = Field::NameFromGetter(function_name_); function_ = cls_.LookupFunctionAllowPrivate(function_name2_); field_ = cls_.LookupFieldAllowPrivate(function_name2_); if (!function_.IsNull() && !function_.is_static()) { // Maybe this was a method extractor. function2_ = Resolver::ResolveDynamicAnyArgs(zone_, cls_, function_name_); if (!function2_.IsNull()) { error_ = CompileFunction(function2_); if (error_.IsError()) { if (FLAG_trace_compilation_trace) { THR_Print( "Compilation trace: error compiling extractor %s for " "%s,%s,%s (%s)\n", function2_.ToCString(), uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); } return error_.raw(); } } } } if (field_.IsNull() && is_getter) { function_name2_ = Field::NameFromGetter(function_name_); field_ = cls_.LookupFieldAllowPrivate(function_name2_); } if (field_.IsNull() && is_init) { function_name2_ = Field::NameFromInit(function_name_); field_ = cls_.LookupFieldAllowPrivate(function_name2_); } } if (!field_.IsNull() && field_.is_const() && field_.is_static() && (field_.StaticValue() == Object::sentinel().raw())) { processed = true; error_ = field_.Initialize(); if (error_.IsError()) { if (FLAG_trace_compilation_trace) { THR_Print( "Compilation trace: error initializing field %s for %s,%s,%s " "(%s)\n", field_.ToCString(), uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); } return error_.raw(); } } if (!function_.IsNull()) { processed = true; error_ = CompileFunction(function_); if (error_.IsError()) { if (FLAG_trace_compilation_trace) { THR_Print("Compilation trace: error compiling %s,%s,%s (%s)\n", uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); } return error_.raw(); } if (add_closure) { function_ = function_.ImplicitClosureFunction(); error_ = CompileFunction(function_); if (error_.IsError()) { if (FLAG_trace_compilation_trace) { THR_Print( "Compilation trace: error compiling closure %s,%s,%s (%s)\n", uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); } return error_.raw(); } } else if (is_dyn) { function_name_ = function_.name(); // With private mangling. function_name_ = Function::CreateDynamicInvocationForwarderName(function_name_); function_ = function_.GetDynamicInvocationForwarder(function_name_); error_ = CompileFunction(function_); if (error_.IsError()) { if (FLAG_trace_compilation_trace) { THR_Print( "Compilation trace: error compiling dynamic forwarder %s,%s,%s " "(%s)\n", uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); } return error_.raw(); } } } if (!field_.IsNull() && field_.is_static() && !field_.is_const() && field_.has_initializer()) { processed = true; function_ = field_.EnsureInitializerFunction(); error_ = CompileFunction(function_); if (error_.IsError()) { if (FLAG_trace_compilation_trace) { THR_Print( "Compilation trace: error compiling initializer %s,%s,%s (%s)\n", uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); } return error_.raw(); } } if (FLAG_trace_compilation_trace) { if (!processed) { THR_Print("Compilation trace: ignored %s,%s,%s\n", uri_.ToCString(), class_name_.ToCString(), function_name_.ToCString()); } } return Object::null(); } RawObject* CompilationTraceLoader::CompileFunction(const Function& function) { if (function.is_abstract() || function.HasCode()) { return Object::null(); } error_ = Compiler::CompileFunction(thread_, function); if (error_.IsError()) { return error_.raw(); } SpeculateInstanceCallTargets(function); return error_.raw(); } // For instance calls, if the receiver's static type has one concrete // implementation, lookup the target for that implementation and add it // to the ICData's entries. // For some well-known interfaces, do the same for the most common concrete // implementation (e.g., int -> _Smi). void CompilationTraceLoader::SpeculateInstanceCallTargets( const Function& function) { sites_ = function.ic_data_array(); if (sites_.IsNull()) { return; } for (intptr_t i = 1; i < sites_.Length(); i++) { site_ ^= sites_.At(i); if (site_.rebind_rule() != ICData::kInstance) { continue; } if (site_.NumArgsTested() != 1) { continue; } static_type_ = site_.receivers_static_type(); if (static_type_.IsNull()) { continue; } else if (static_type_.IsDoubleType()) { receiver_cls_ = Isolate::Current()->class_table()->At(kDoubleCid); } else if (static_type_.IsIntType()) { receiver_cls_ = Isolate::Current()->class_table()->At(kSmiCid); } else if (static_type_.IsStringType()) { receiver_cls_ = Isolate::Current()->class_table()->At(kOneByteStringCid); } else if (static_type_.IsDartFunctionType() || static_type_.IsDartClosureType()) { receiver_cls_ = Isolate::Current()->class_table()->At(kClosureCid); } else if (static_type_.HasTypeClass()) { receiver_cls_ = static_type_.type_class(); if (receiver_cls_.is_implemented() || receiver_cls_.is_abstract()) { continue; } } else { continue; } selector_ = site_.target_name(); args_desc_ = site_.arguments_descriptor(); target_ = Resolver::ResolveDynamicForReceiverClass( receiver_cls_, selector_, ArgumentsDescriptor(args_desc_)); if (!target_.IsNull() && !site_.HasReceiverClassId(receiver_cls_.id())) { intptr_t count = 0; // Don't pollute type feedback and coverage data. site_.AddReceiverCheck(receiver_cls_.id(), target_, count); } } } TypeFeedbackSaver::TypeFeedbackSaver(WriteStream* stream) : stream_(stream), cls_(Class::Handle()), lib_(Library::Handle()), str_(String::Handle()), fields_(Array::Handle()), field_(Field::Handle()), code_(Code::Handle()), call_sites_(Array::Handle()), call_site_(ICData::Handle()) {} // These flags affect deopt ids. static char* CompilerFlags() { TextBuffer buffer(64); #define ADD_FLAG(flag) buffer.AddString(FLAG_##flag ? " " #flag : " no-" #flag) ADD_FLAG(enable_asserts); ADD_FLAG(use_field_guards); ADD_FLAG(use_osr); ADD_FLAG(causal_async_stacks); ADD_FLAG(fields_may_be_reset); #undef ADD_FLAG buffer.AddString(FLAG_use_bytecode_compiler || FLAG_enable_interpreter ? " bytecode" : " no-bytecode"); return buffer.Steal(); } void TypeFeedbackSaver::WriteHeader() { const char* expected_version = Version::SnapshotString(); ASSERT(expected_version != NULL); const intptr_t version_len = strlen(expected_version); stream_->WriteBytes(reinterpret_cast(expected_version), version_len); char* expected_features = CompilerFlags(); ASSERT(expected_features != NULL); const intptr_t features_len = strlen(expected_features); stream_->WriteBytes(reinterpret_cast(expected_features), features_len + 1); free(expected_features); } void TypeFeedbackSaver::SaveClasses() { ClassTable* table = Isolate::Current()->class_table(); intptr_t num_cids = table->NumCids(); WriteInt(num_cids); for (intptr_t cid = kNumPredefinedCids; cid < num_cids; cid++) { cls_ = table->At(cid); WriteClassByName(cls_); } } void TypeFeedbackSaver::SaveFields() { ClassTable* table = Isolate::Current()->class_table(); intptr_t num_cids = table->NumCids(); for (intptr_t cid = kNumPredefinedCids; cid < num_cids; cid++) { cls_ = table->At(cid); WriteClassByName(cls_); fields_ = cls_.fields(); WriteInt(fields_.Length()); for (intptr_t i = 0; i < fields_.Length(); i++) { field_ ^= fields_.At(i); str_ = field_.name(); str_ = String::RemovePrivateKey(str_); WriteString(str_); WriteInt(field_.guarded_cid()); WriteInt(field_.is_nullable()); } } } void TypeFeedbackSaver::Visit(const Function& function) { if (!function.HasCode()) { return; // Not compiled. } cls_ = function.Owner(); WriteClassByName(cls_); str_ = function.name(); str_ = String::RemovePrivateKey(str_); WriteString(str_); WriteInt(function.kind()); WriteInt(function.token_pos().value()); code_ = function.CurrentCode(); intptr_t usage = function.usage_counter(); if (usage < 0) { // Usage is set to INT_MIN while in the background compilation queue ... usage = (usage - INT_MIN) + FLAG_optimization_counter_threshold; } else if (code_.is_optimized()) { // ... and set to 0 when an optimizing compile completes. usage = usage + FLAG_optimization_counter_threshold; } WriteInt(usage); WriteInt(function.inlining_depth()); call_sites_ = function.ic_data_array(); if (call_sites_.IsNull()) { call_sites_ = Object::empty_array().raw(); // Remove edge case. } // First element is edge counters. WriteInt(call_sites_.Length() - 1); for (intptr_t i = 1; i < call_sites_.Length(); i++) { call_site_ ^= call_sites_.At(i); WriteInt(call_site_.deopt_id()); WriteInt(call_site_.rebind_rule()); str_ = call_site_.target_name(); str_ = String::RemovePrivateKey(str_); WriteString(str_); intptr_t num_checked_arguments = call_site_.NumArgsTested(); WriteInt(num_checked_arguments); intptr_t num_entries = call_site_.NumberOfChecks(); WriteInt(num_entries); for (intptr_t entry_index = 0; entry_index < num_entries; entry_index++) { WriteInt(call_site_.GetCountAt(entry_index)); for (intptr_t argument_index = 0; argument_index < num_checked_arguments; argument_index++) { WriteInt(call_site_.GetClassIdAt(entry_index, argument_index)); } } } } void TypeFeedbackSaver::WriteClassByName(const Class& cls) { lib_ = cls.library(); str_ = lib_.url(); WriteString(str_); str_ = cls_.Name(); str_ = String::RemovePrivateKey(str_); WriteString(str_); } void TypeFeedbackSaver::WriteString(const String& value) { const char* cstr = value.ToCString(); intptr_t len = strlen(cstr); stream_->WriteUnsigned(len); stream_->WriteBytes(cstr, len); } TypeFeedbackLoader::TypeFeedbackLoader(Thread* thread) : thread_(thread), zone_(thread->zone()), stream_(nullptr), cid_map_(nullptr), uri_(String::Handle(zone_)), lib_(Library::Handle(zone_)), cls_name_(String::Handle(zone_)), cls_(Class::Handle(zone_)), field_name_(String::Handle(zone_)), fields_(Array::Handle(zone_)), field_(Field::Handle(zone_)), func_name_(String::Handle(zone_)), func_(Function::Handle(zone_)), call_sites_(Array::Handle(zone_)), call_site_(ICData::Handle(zone_)), target_name_(String::Handle(zone_)), target_(Function::Handle(zone_)), args_desc_(Array::Handle(zone_)), functions_to_compile_( GrowableObjectArray::Handle(zone_, GrowableObjectArray::New())), error_(Error::Handle(zone_)) {} TypeFeedbackLoader::~TypeFeedbackLoader() { delete[] cid_map_; } RawObject* TypeFeedbackLoader::LoadFeedback(ReadStream* stream) { stream_ = stream; error_ = CheckHeader(); if (error_.IsError()) { return error_.raw(); } error_ = LoadClasses(); if (error_.IsError()) { return error_.raw(); } error_ = LoadFields(); if (error_.IsError()) { return error_.raw(); } while (stream_->PendingBytes() > 0) { error_ = LoadFunction(); if (error_.IsError()) { return error_.raw(); } } while (functions_to_compile_.Length() > 0) { func_ ^= functions_to_compile_.RemoveLast(); if (Compiler::CanOptimizeFunction(thread_, func_) && (func_.usage_counter() >= FLAG_optimization_counter_threshold)) { error_ = Compiler::CompileOptimizedFunction(thread_, func_); if (error_.IsError()) { return error_.raw(); } } } if (FLAG_trace_compilation_trace) { THR_Print("Done loading feedback\n"); } return Error::null(); } RawObject* TypeFeedbackLoader::CheckHeader() { const char* expected_version = Version::SnapshotString(); ASSERT(expected_version != NULL); const intptr_t version_len = strlen(expected_version); if (stream_->PendingBytes() < version_len) { const intptr_t kMessageBufferSize = 128; char message_buffer[kMessageBufferSize]; Utils::SNPrint(message_buffer, kMessageBufferSize, "No snapshot version found, expected '%s'", expected_version); const String& msg = String::Handle(String::New(message_buffer, Heap::kOld)); return ApiError::New(msg, Heap::kOld); } const char* version = reinterpret_cast(stream_->AddressOfCurrentPosition()); ASSERT(version != NULL); if (strncmp(version, expected_version, version_len)) { const intptr_t kMessageBufferSize = 256; char message_buffer[kMessageBufferSize]; char* actual_version = Utils::StrNDup(version, version_len); Utils::SNPrint(message_buffer, kMessageBufferSize, "Wrong snapshot version, expected '%s' found '%s'", expected_version, actual_version); free(actual_version); const String& msg = String::Handle(String::New(message_buffer, Heap::kOld)); return ApiError::New(msg, Heap::kOld); } stream_->Advance(version_len); char* expected_features = CompilerFlags(); ASSERT(expected_features != NULL); const intptr_t expected_len = strlen(expected_features); const char* features = reinterpret_cast(stream_->AddressOfCurrentPosition()); ASSERT(features != NULL); intptr_t buffer_len = Utils::StrNLen(features, stream_->PendingBytes()); if ((buffer_len != expected_len) || strncmp(features, expected_features, expected_len)) { const String& msg = String::Handle(String::NewFormatted( Heap::kOld, "Feedback not compatible with the current VM configuration: " "the feedback requires '%.*s' but the VM has '%s'", static_cast(buffer_len > 1024 ? 1024 : buffer_len), features, expected_features)); free(expected_features); return ApiError::New(msg, Heap::kOld); } free(expected_features); stream_->Advance(expected_len + 1); return Error::null(); } RawObject* TypeFeedbackLoader::LoadClasses() { num_cids_ = ReadInt(); cid_map_ = new intptr_t[num_cids_]; for (intptr_t cid = 0; cid < num_cids_; cid++) { cid_map_[cid] = kIllegalCid; } for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) { cid_map_[cid] = cid; } for (intptr_t cid = kNumPredefinedCids; cid < num_cids_; cid++) { cls_ = ReadClassByName(); if (!cls_.IsNull()) { cid_map_[cid] = cls_.id(); } } return Error::null(); } RawObject* TypeFeedbackLoader::LoadFields() { for (intptr_t cid = kNumPredefinedCids; cid < num_cids_; cid++) { cls_ = ReadClassByName(); bool skip = cls_.IsNull(); intptr_t num_fields = ReadInt(); if (!skip && (num_fields > 0)) { error_ = cls_.EnsureIsFinalized(thread_); if (error_.IsError()) { return error_.raw(); } fields_ = cls_.fields(); } for (intptr_t i = 0; i < num_fields; i++) { field_name_ = ReadString(); intptr_t guarded_cid = cid_map_[ReadInt()]; intptr_t is_nullable = ReadInt(); if (skip) { continue; } if (i >= fields_.Length()) { if (FLAG_trace_compilation_trace) { THR_Print("Missing field %s\n", field_name_.ToCString()); } continue; } field_ ^= fields_.At(i); if (!String::EqualsIgnoringPrivateKey(String::Handle(field_.name()), field_name_)) { if (FLAG_trace_compilation_trace) { THR_Print("Missing field %s\n", field_name_.ToCString()); } continue; } if (guarded_cid == kIllegalCid) { // Guarded CID from feedback is not in current program: assume the field // will become polymorphic. field_.set_guarded_cid(kDynamicCid); field_.set_is_nullable(true); } else if ((field_.guarded_cid() != kIllegalCid) && (field_.guarded_cid() == guarded_cid)) { // Guarded CID from feedback is different from initialized guarded CID // in the current program: assume the field will become polymorphic. field_.set_guarded_cid(kDynamicCid); field_.set_is_nullable(true); } else { field_.set_guarded_cid(guarded_cid); field_.set_is_nullable(is_nullable || field_.is_nullable()); } // TODO(rmacnak): Merge other field type feedback. field_.set_guarded_list_length(Field::kNoFixedLength); field_.set_guarded_list_length_in_object_offset( Field::kUnknownLengthOffset); field_.set_static_type_exactness_state( StaticTypeExactnessState::NotTracking()); field_.DeoptimizeDependentCode(); } } return Error::null(); } RawObject* TypeFeedbackLoader::LoadFunction() { bool skip = false; cls_ = ReadClassByName(); if (!cls_.IsNull()) { error_ = cls_.EnsureIsFinalized(thread_); if (error_.IsError()) { return error_.raw(); } } else { skip = true; } func_name_ = ReadString(); // Without private mangling. RawFunction::Kind kind = static_cast(ReadInt()); intptr_t token_pos = ReadInt(); intptr_t usage = ReadInt(); intptr_t inlining_depth = ReadInt(); intptr_t num_call_sites = ReadInt(); if (!skip) { func_ = FindFunction(kind, token_pos); if (func_.IsNull()) { skip = true; if (FLAG_trace_compilation_trace) { THR_Print("Missing function %s %s\n", func_name_.ToCString(), Function::KindToCString(kind)); } } } if (!skip) { error_ = Compiler::CompileFunction(thread_, func_); if (error_.IsError()) { return error_.raw(); } call_sites_ = func_.ic_data_array(); if (call_sites_.IsNull()) { call_sites_ = Object::empty_array().raw(); // Remove edge case. } if (call_sites_.Length() != num_call_sites + 1) { skip = true; if (FLAG_trace_compilation_trace) { THR_Print("Mismatched call site count %s %" Pd " %" Pd "\n", func_name_.ToCString(), call_sites_.Length(), num_call_sites); } } } // First element is edge counters. for (intptr_t i = 1; i <= num_call_sites; i++) { intptr_t deopt_id = ReadInt(); intptr_t rebind_rule = ReadInt(); target_name_ = ReadString(); intptr_t num_checked_arguments = ReadInt(); intptr_t num_entries = ReadInt(); if (!skip) { call_site_ ^= call_sites_.At(i); if ((call_site_.deopt_id() != deopt_id) || (call_site_.rebind_rule() != rebind_rule) || (call_site_.NumArgsTested() != num_checked_arguments)) { skip = true; if (FLAG_trace_compilation_trace) { THR_Print("Mismatched call site %s\n", call_site_.ToCString()); } } } for (intptr_t entry_index = 0; entry_index < num_entries; entry_index++) { intptr_t entry_usage = ReadInt(); bool skip_entry = skip; GrowableArray cids(num_checked_arguments); for (intptr_t argument_index = 0; argument_index < num_checked_arguments; argument_index++) { intptr_t cid = cid_map_[ReadInt()]; cids.Add(cid); if (cid == kIllegalCid) { // Alternative: switch to a sentinel value such as kDynamicCid and // have the optimizer generate a megamorphic call. skip_entry = true; } } if (skip_entry) { continue; } intptr_t reuse_index = call_site_.FindCheck(cids); if (reuse_index == -1) { cls_ = thread_->isolate()->class_table()->At(cids[0]); // Use target name and args descriptor from the current program // instead of saved feedback to get the correct private mangling and // ensure no arity mismatch crashes. target_name_ = call_site_.target_name(); args_desc_ = call_site_.arguments_descriptor(); target_ = Resolver::ResolveDynamicForReceiverClass( cls_, target_name_, ArgumentsDescriptor(args_desc_)); if (!target_.IsNull()) { if (num_checked_arguments == 1) { call_site_.AddReceiverCheck(cids[0], target_, entry_usage); } else { call_site_.AddCheck(cids, target_, entry_usage); } } } else { call_site_.IncrementCountAt(reuse_index, entry_usage); } } } if (!skip) { func_.set_usage_counter(usage); func_.set_inlining_depth(inlining_depth); // Delay compilation until all feedback is loaded so feedback is available // for inlined functions. functions_to_compile_.Add(func_); } return Error::null(); } RawFunction* TypeFeedbackLoader::FindFunction(RawFunction::Kind kind, intptr_t token_pos) { if (cls_name_.Equals(Symbols::TopLevel())) { func_ = lib_.LookupFunctionAllowPrivate(func_name_); } else { func_ = cls_.LookupFunctionAllowPrivate(func_name_); } if (!func_.IsNull()) { // Found regular method. } else if (kind == RawFunction::kMethodExtractor) { ASSERT(Field::IsGetterName(func_name_)); // Without private mangling: String& name = String::Handle(zone_, Field::NameFromGetter(func_name_)); func_ = cls_.LookupFunctionAllowPrivate(name); if (!func_.IsNull() && func_.IsDynamicFunction()) { name = func_.name(); // With private mangling. name = Field::GetterName(name); func_ = func_.GetMethodExtractor(name); } else { func_ = Function::null(); } } else if (kind == RawFunction::kDynamicInvocationForwarder) { // Without private mangling: String& name = String::Handle( zone_, Function::DemangleDynamicInvocationForwarderName(func_name_)); func_ = cls_.LookupFunctionAllowPrivate(name); if (!func_.IsNull() && func_.IsDynamicFunction()) { name = func_.name(); // With private mangling. name = Function::CreateDynamicInvocationForwarderName(name); func_ = func_.CreateDynamicInvocationForwarder(name); } else { func_ = Function::null(); } } else if (kind == RawFunction::kClosureFunction) { // Note this lookup relies on parent functions appearing before child // functions in the serialized feedback, so the parent will have already // been unoptimized compilated and the child function created and added to // ObjectStore::closure_functions_. const GrowableObjectArray& closure_functions = GrowableObjectArray::Handle( zone_, thread_->isolate()->object_store()->closure_functions()); bool found = false; for (intptr_t i = 0; i < closure_functions.Length(); i++) { func_ ^= closure_functions.At(i); if ((func_.Owner() == cls_.raw()) && (func_.token_pos().value() == token_pos)) { found = true; break; } } if (!found) { func_ = Function::null(); } } else { // This leaves unhandled: // - kInvokeFieldDispatcher (how to get a valid args descriptor?) // - static field getters // - static field initializers (not retained by the field object) } if (!func_.IsNull()) { if (kind == RawFunction::kImplicitClosureFunction) { func_ = func_.ImplicitClosureFunction(); } if (func_.is_abstract() || (func_.kind() != kind)) { func_ = Function::null(); } } return func_.raw(); } RawClass* TypeFeedbackLoader::ReadClassByName() { uri_ = ReadString(); cls_name_ = ReadString(); lib_ = Library::LookupLibrary(thread_, uri_); if (lib_.IsNull()) { if (FLAG_trace_compilation_trace) { THR_Print("Missing library %s\n", uri_.ToCString()); } return Class::null(); } if (cls_name_.Equals(Symbols::TopLevel())) { cls_ = lib_.toplevel_class(); } else { cls_ = lib_.SlowLookupClassAllowMultiPartPrivate(cls_name_); if (cls_.IsNull()) { if (FLAG_trace_compilation_trace) { THR_Print("Missing class %s %s\n", uri_.ToCString(), cls_name_.ToCString()); } } } return cls_.raw(); } RawString* TypeFeedbackLoader::ReadString() { intptr_t len = stream_->ReadUnsigned(); const char* cstr = reinterpret_cast(stream_->AddressOfCurrentPosition()); stream_->Advance(len); return Symbols::New(thread_, cstr, len); } #endif // !defined(DART_PRECOMPILED_RUNTIME) } // namespace dart