blob: 0db337d5ea6800efbdbc32e3d6d872582af501ef [file] [log] [blame]
// 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::VisitFunction(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;
}
ObjectPtr CompilationTraceLoader::CompileTrace(uint8_t* buffer, intptr_t size) {
// First compile functions named in the trace.
char* cursor = reinterpret_cast<char*>(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;
// TODO(dartbug.com/33549): Update this code to use the size of the
// parameters when supporting calls to closures with unboxed parameters.
arguments_descriptor = ArgumentsDescriptor::NewBoxed(kTypeArgsLen, argc);
dispatcher = closure_class.GetInvocationDispatcher(
Symbols::Call(), arguments_descriptor,
FunctionLayout::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.
ObjectPtr 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_.InitializeStatic();
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_nontrivial_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();
}
ObjectPtr 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(BaseWriteStream* 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
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<const uint8_t*>(expected_version),
version_len);
char* expected_features = CompilerFlags();
ASSERT(expected_features != NULL);
const intptr_t features_len = strlen(expected_features);
stream_->WriteBytes(reinterpret_cast<const uint8_t*>(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(static_cast<intptr_t>(field_.is_nullable()));
}
}
}
void TypeFeedbackSaver::VisitFunction(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 INT32_MIN while in the background compilation queue ...
usage = (usage - INT32_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_;
}
ObjectPtr 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();
}
ObjectPtr 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<const char*>(stream_->AddressOfCurrentPosition());
ASSERT(version != NULL);
if (strncmp(version, expected_version, version_len) != 0) {
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<const char*>(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) != 0)) {
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<int>(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();
}
ObjectPtr 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();
}
ObjectPtr 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 != 0) || 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();
}
ObjectPtr 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.
FunctionLayout::Kind kind = static_cast<FunctionLayout::Kind>(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<intptr_t> 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();
if (cls_.EnsureIsFinalized(thread_) == Error::null()) {
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();
}
FunctionPtr TypeFeedbackLoader::FindFunction(FunctionLayout::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 == FunctionLayout::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 == FunctionLayout::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 == FunctionLayout::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 == FunctionLayout::kImplicitClosureFunction) {
func_ = func_.ImplicitClosureFunction();
}
if (func_.is_abstract() || (func_.kind() != kind)) {
func_ = Function::null();
}
}
return func_.raw();
}
ClassPtr 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();
}
StringPtr TypeFeedbackLoader::ReadString() {
intptr_t len = stream_->ReadUnsigned();
const char* cstr =
reinterpret_cast<const char*>(stream_->AddressOfCurrentPosition());
stream_->Advance(len);
return Symbols::New(thread_, cstr, len);
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
} // namespace dart