[vm] When loading a compilation trace, speculatively populate ICData based on the receiver's static type.

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).

Avoids method resolution during application startup.

Bug: https://github.com/dart-lang/sdk/issues/36428
Change-Id: I57edf9c9cb5cb13af3182a100adce2802571a3aa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/98445
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
This commit is contained in:
Ryan Macnak 2019-04-05 21:12:44 +00:00 committed by commit-bot@chromium.org
parent f93b93dd91
commit 9e1adc54a7
16 changed files with 158 additions and 71 deletions

View file

@ -60,6 +60,13 @@ CompilationTraceLoader::CompilationTraceLoader(Thread* thread)
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) {
@ -354,14 +361,73 @@ RawObject* CompilationTraceLoader::CompileTriple(const char* uri_cstr,
}
RawObject* CompilationTraceLoader::CompileFunction(const Function& function) {
if (function.is_abstract()) {
if (function.is_abstract() || function.HasCode()) {
return Object::null();
}
// Prevent premature code collection due to major GC during startup.
if (function.usage_counter() < Function::kGraceUsageCounter) {
function.set_usage_counter(Function::kGraceUsageCounter);
}
return Compiler::CompileFunction(thread_, function);
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();
ASSERT(!sites_.IsNull());
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)

View file

@ -42,6 +42,7 @@ class CompilationTraceLoader : public ValueObject {
const char* cls_cstr,
const char* func_cstr);
RawObject* CompileFunction(const Function& function);
void SpeculateInstanceCallTargets(const Function& function);
Thread* thread_;
Zone* zone_;
@ -54,6 +55,13 @@ class CompilationTraceLoader : public ValueObject {
Function& function_;
Function& function2_;
Field& field_;
Array& sites_;
ICData& site_;
AbstractType& static_type_;
Class& receiver_cls_;
Function& target_;
String& selector_;
Array& args_desc_;
Object& error_;
};

View file

@ -567,7 +567,7 @@ void FlowGraph::AddExactnessGuard(InstanceCallInstr* call,
InsertBefore(call, load_type_args, call->env(), FlowGraph::kValue);
const AbstractType& type =
AbstractType::Handle(zone(), call->ic_data()->StaticReceiverType());
AbstractType::Handle(zone(), call->ic_data()->receivers_static_type());
ASSERT(!type.IsNull());
const TypeArguments& args = TypeArguments::Handle(zone(), type.arguments());
Instruction* guard = new (zone()) CheckConditionInstr(

View file

@ -1259,7 +1259,7 @@ static const Code& StubEntryFor(const ICData& ic_data, bool optimized) {
switch (ic_data.NumArgsTested()) {
case 1:
#if defined(TARGET_ARCH_X64)
if (ic_data.IsTrackingExactness()) {
if (ic_data.is_tracking_exactness()) {
if (optimized) {
return StubCode::OneArgOptimizedCheckInlineCacheWithExactnessCheck();
} else {
@ -1268,12 +1268,12 @@ static const Code& StubEntryFor(const ICData& ic_data, bool optimized) {
}
#else
// TODO(dartbug.com/34170) Port exactness tracking to other platforms.
ASSERT(!ic_data.IsTrackingExactness());
ASSERT(!ic_data.is_tracking_exactness());
#endif
return optimized ? StubCode::OneArgOptimizedCheckInlineCache()
: StubCode::OneArgCheckInlineCache();
case 2:
ASSERT(!ic_data.IsTrackingExactness());
ASSERT(!ic_data.is_tracking_exactness());
return optimized ? StubCode::TwoArgsOptimizedCheckInlineCache()
: StubCode::TwoArgsCheckInlineCache();
default:
@ -1759,7 +1759,7 @@ const ICData* FlowGraphCompiler::GetOrAddInstanceCallICData(
ASSERT(res->TypeArgsLen() ==
ArgumentsDescriptor(arguments_descriptor).TypeArgsLen());
ASSERT(!res->is_static_call());
ASSERT(res->StaticReceiverType() == receiver_type.raw());
ASSERT(res->receivers_static_type() == receiver_type.raw());
return res;
}
const ICData& ic_data = ICData::ZoneHandle(

View file

@ -4107,26 +4107,14 @@ void InstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
const Array& arguments_descriptor =
Array::Handle(zone, GetArgumentsDescriptor());
AbstractType& static_receiver_type = AbstractType::Handle(zone);
#if defined(TARGET_ARCH_X64)
// Enable static type exactness tracking for the callsite by setting
// static receiver type on the ICData.
if (checked_argument_count() == 1) {
if (static_receiver_type_ != nullptr &&
static_receiver_type_->HasTypeClass()) {
const Class& cls =
Class::Handle(zone, static_receiver_type_->type_class());
if (cls.IsGeneric() && !cls.IsFutureOrClass()) {
static_receiver_type = static_receiver_type_->raw();
}
}
AbstractType& receivers_static_type = AbstractType::Handle(zone);
if (receivers_static_type_ != nullptr) {
receivers_static_type = receivers_static_type_->raw();
}
#endif
call_ic_data = compiler->GetOrAddInstanceCallICData(
deopt_id(), function_name(), arguments_descriptor,
checked_argument_count(), static_receiver_type);
checked_argument_count(), receivers_static_type);
} else {
call_ic_data = &ICData::ZoneHandle(zone, ic_data()->raw());
}

View file

@ -3327,9 +3327,9 @@ class InstanceCallInstr : public TemplateDartCall<0> {
intptr_t checked_argument_count() const { return checked_argument_count_; }
const Function& interface_target() const { return interface_target_; }
void set_static_receiver_type(const AbstractType* receiver_type) {
ASSERT(receiver_type != nullptr && receiver_type->IsInstantiated());
static_receiver_type_ = receiver_type;
void set_receivers_static_type(const AbstractType* receiver_type) {
ASSERT(receiver_type != nullptr);
receivers_static_type_ = receiver_type;
}
bool has_unique_selector() const { return has_unique_selector_; }
@ -3390,7 +3390,7 @@ class InstanceCallInstr : public TemplateDartCall<0> {
bool has_unique_selector_;
Code::EntryKind entry_kind_ = Code::EntryKind::kNormal;
const AbstractType* static_receiver_type_ = nullptr;
const AbstractType* receivers_static_type_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(InstanceCallInstr);
};

View file

@ -252,9 +252,9 @@ static void PrintICDataHelper(BufferFormatter* f,
const ICData& ic_data,
intptr_t num_checks_to_print) {
f->Print(" IC[");
if (ic_data.IsTrackingExactness()) {
if (ic_data.is_tracking_exactness()) {
f->Print("(%s) ",
AbstractType::Handle(ic_data.StaticReceiverType()).ToCString());
AbstractType::Handle(ic_data.receivers_static_type()).ToCString());
}
f->Print("%" Pd ": ", ic_data.NumberOfChecks());
Function& target = Function::Handle();
@ -278,7 +278,7 @@ static void PrintICDataHelper(BufferFormatter* f,
f->Print("%s", String::Handle(cls.Name()).ToCString());
}
f->Print(" cnt:%" Pd " trgt:'%s'", count, target.ToQualifiedCString());
if (ic_data.IsTrackingExactness()) {
if (ic_data.is_tracking_exactness()) {
f->Print(" %s", ic_data.GetExactnessAt(i).ToCString());
}
}

View file

@ -357,7 +357,12 @@ Fragment FlowGraphBuilder::InstanceCall(
}
if (call_site_attrs != nullptr && call_site_attrs->receiver_type != nullptr &&
call_site_attrs->receiver_type->IsInstantiated()) {
call->set_static_receiver_type(call_site_attrs->receiver_type);
call->set_receivers_static_type(call_site_attrs->receiver_type);
} else if (!interface_target.IsNull()) {
const Class& owner = Class::Handle(Z, interface_target.Owner());
const AbstractType& type =
AbstractType::ZoneHandle(Z, owner.DeclarationType());
call->set_receivers_static_type(&type);
}
Push(call);
return Fragment(call);

View file

@ -348,8 +348,8 @@ word ICData::entries_offset() {
return dart::ICData::entries_offset();
}
word ICData::static_receiver_type_offset() {
return dart::ICData::static_receiver_type_offset();
word ICData::receivers_static_type_offset() {
return dart::ICData::receivers_static_type_offset();
}
word ICData::state_bits_offset() {

View file

@ -411,7 +411,7 @@ class ICData : public AllStatic {
static word owner_offset();
static word arguments_descriptor_offset();
static word entries_offset();
static word static_receiver_type_offset();
static word receivers_static_type_offset();
static word state_bits_offset();
static word CodeIndexFor(word num_args);

View file

@ -2017,10 +2017,10 @@ void StubCodeCompiler::GenerateNArgsCheckInlineCacheStub(
__ j(EQUAL, &call_target_function_through_unchecked_entry);
// Check trivial exactness.
// Note: RawICData::static_receiver_type_ is guaranteed to be not null
// Note: RawICData::receivers_static_type_ is guaranteed to be not null
// because we only emit calls to this stub when it is not null.
__ movq(RCX,
FieldAddress(RBX, target::ICData::static_receiver_type_offset()));
FieldAddress(RBX, target::ICData::receivers_static_type_offset()));
__ movq(RCX, FieldAddress(RCX, target::Type::arguments_offset()));
// RAX contains an offset to type arguments in words as a smi,
// hence TIMES_4. RDX is guaranteed to be non-smi because it is expected to

View file

@ -13146,14 +13146,24 @@ RawUnlinkedCall* UnlinkedCall::New() {
}
#if !defined(DART_PRECOMPILED_RUNTIME)
void ICData::SetStaticReceiverType(const AbstractType& type) const {
StorePointer(&raw_ptr()->static_receiver_type_, type.raw());
void ICData::SetReceiversStaticType(const AbstractType& type) const {
StorePointer(&raw_ptr()->receivers_static_type_, type.raw());
#if defined(TARGET_ARCH_X64)
if (!type.IsNull() && type.HasTypeClass() && (NumArgsTested() == 1) &&
type.IsInstantiated()) {
const Class& cls = Class::Handle(type.type_class());
if (cls.IsGeneric() && !cls.IsFutureOrClass()) {
set_tracking_exactness(true);
}
}
#endif // defined(TARGET_ARCH_X64)
}
#endif
void ICData::ResetSwitchable(Zone* zone) const {
ASSERT(NumArgsTested() == 1);
ASSERT(!IsTrackingExactness());
ASSERT(!is_tracking_exactness());
set_entries(Array::Handle(zone, CachedEmptyICDataArray(1, false)));
}
@ -13298,7 +13308,7 @@ intptr_t ICData::TestEntryLengthFor(intptr_t num_args,
}
intptr_t ICData::TestEntryLength() const {
return TestEntryLengthFor(NumArgsTested(), IsTrackingExactness());
return TestEntryLengthFor(NumArgsTested(), is_tracking_exactness());
}
intptr_t ICData::Length() const {
@ -13535,7 +13545,7 @@ bool ICData::ValidateInterceptor(const Function& target) const {
void ICData::AddCheck(const GrowableArray<intptr_t>& class_ids,
const Function& target,
intptr_t count) const {
ASSERT(!IsTrackingExactness());
ASSERT(!is_tracking_exactness());
ASSERT(!target.IsNull());
ASSERT((target.name() == target_name()) || ValidateInterceptor(target));
DEBUG_ASSERT(!HasCheck(class_ids));
@ -13648,7 +13658,7 @@ void ICData::AddReceiverCheck(intptr_t receiver_class_id,
if (Isolate::Current()->compilation_allowed()) {
data.SetAt(data_pos + 1, target);
data.SetAt(data_pos + 2, Smi::Handle(Smi::New(count)));
if (IsTrackingExactness()) {
if (is_tracking_exactness()) {
data.SetAt(data_pos + 3, Smi::Handle(Smi::New(exactness.Encode())));
}
} else {
@ -13666,7 +13676,7 @@ void ICData::AddReceiverCheck(intptr_t receiver_class_id,
}
StaticTypeExactnessState ICData::GetExactnessAt(intptr_t index) const {
if (!IsTrackingExactness()) {
if (!is_tracking_exactness()) {
return StaticTypeExactnessState::NotTracking();
}
const Array& data = Array::Handle(entries());
@ -14086,7 +14096,7 @@ RawICData* ICData::NewDescriptor(Zone* zone,
intptr_t deopt_id,
intptr_t num_args_tested,
RebindRule rebind_rule,
const AbstractType& static_receiver_type) {
const AbstractType& receivers_static_type) {
ASSERT(!owner.IsNull());
ASSERT(!target_name.IsNull());
ASSERT(!arguments_descriptor.IsNull());
@ -14107,7 +14117,7 @@ RawICData* ICData::NewDescriptor(Zone* zone,
result.set_state_bits(0);
result.set_rebind_rule(rebind_rule);
result.SetNumArgsTested(num_args_tested);
NOT_IN_PRECOMPILED(result.SetStaticReceiverType(static_receiver_type));
NOT_IN_PRECOMPILED(result.SetReceiversStaticType(receivers_static_type));
return result.raw();
}
@ -14136,15 +14146,15 @@ RawICData* ICData::New(const Function& owner,
intptr_t deopt_id,
intptr_t num_args_tested,
RebindRule rebind_rule,
const AbstractType& static_receiver_type) {
const AbstractType& receivers_static_type) {
Zone* zone = Thread::Current()->zone();
const ICData& result = ICData::Handle(
zone,
NewDescriptor(zone, owner, target_name, arguments_descriptor, deopt_id,
num_args_tested, rebind_rule, static_receiver_type));
num_args_tested, rebind_rule, receivers_static_type));
result.set_entries(Array::Handle(
zone,
CachedEmptyICDataArray(num_args_tested, result.IsTrackingExactness())));
CachedEmptyICDataArray(num_args_tested, result.is_tracking_exactness())));
return result.raw();
}
@ -14154,7 +14164,7 @@ RawICData* ICData::NewFrom(const ICData& from, intptr_t num_args_tested) {
Function::Handle(from.Owner()), String::Handle(from.target_name()),
Array::Handle(from.arguments_descriptor()), from.deopt_id(),
num_args_tested, from.rebind_rule(),
AbstractType::Handle(from.StaticReceiverType())));
AbstractType::Handle(from.receivers_static_type())));
// Copy deoptimization reasons.
result.SetDeoptReasons(from.DeoptReasons());
return result.raw();
@ -14162,12 +14172,13 @@ RawICData* ICData::NewFrom(const ICData& from, intptr_t num_args_tested) {
RawICData* ICData::Clone(const ICData& from) {
Zone* zone = Thread::Current()->zone();
const ICData& result = ICData::Handle(ICData::NewDescriptor(
zone, Function::Handle(zone, from.Owner()),
String::Handle(zone, from.target_name()),
Array::Handle(zone, from.arguments_descriptor()), from.deopt_id(),
from.NumArgsTested(), from.rebind_rule(),
AbstractType::Handle(from.StaticReceiverType())));
const ICData& result = ICData::Handle(
zone, ICData::NewDescriptor(
zone, Function::Handle(zone, from.Owner()),
String::Handle(zone, from.target_name()),
Array::Handle(zone, from.arguments_descriptor()),
from.deopt_id(), from.NumArgsTested(), from.rebind_rule(),
AbstractType::Handle(zone, from.receivers_static_type())));
// Clone entry array.
const Array& from_array = Array::Handle(zone, from.entries());
const intptr_t len = from_array.Length();

View file

@ -1588,15 +1588,20 @@ class ICData : public Object {
bool IsImmutable() const;
#if !defined(DART_PRECOMPILED_RUNTIME)
RawAbstractType* StaticReceiverType() const {
return raw_ptr()->static_receiver_type_;
RawAbstractType* receivers_static_type() const {
return raw_ptr()->receivers_static_type_;
}
void SetStaticReceiverType(const AbstractType& type) const;
bool IsTrackingExactness() const {
return StaticReceiverType() != Object::null();
void SetReceiversStaticType(const AbstractType& type) const;
bool is_tracking_exactness() const {
return TrackingExactnessBit::decode(raw_ptr()->state_bits_);
}
void set_tracking_exactness(bool value) const {
StoreNonPointer(
&raw_ptr()->state_bits_,
TrackingExactnessBit::update(value, raw_ptr()->state_bits_));
}
#else
bool IsTrackingExactness() const { return false; }
bool is_tracking_exactness() const { return false; }
#endif
void Reset(Zone* zone) const;
@ -1702,8 +1707,8 @@ class ICData : public Object {
static intptr_t owner_offset() { return OFFSET_OF(RawICData, owner_); }
#if !defined(DART_PRECOMPILED_RUNTIME)
static intptr_t static_receiver_type_offset() {
return OFFSET_OF(RawICData, static_receiver_type_);
static intptr_t receivers_static_type_offset() {
return OFFSET_OF(RawICData, receivers_static_type_);
}
#endif
@ -1882,7 +1887,9 @@ class ICData : public Object {
enum {
kNumArgsTestedPos = 0,
kNumArgsTestedSize = 2,
kDeoptReasonPos = kNumArgsTestedPos + kNumArgsTestedSize,
kTrackingExactnessPos = kNumArgsTestedPos + kNumArgsTestedSize,
kTrackingExactnessSize = 1,
kDeoptReasonPos = kTrackingExactnessPos + kTrackingExactnessSize,
kDeoptReasonSize = kLastRecordedDeoptReason + 1,
kRebindRulePos = kDeoptReasonPos + kDeoptReasonSize,
kRebindRuleSize = 3
@ -1894,6 +1901,10 @@ class ICData : public Object {
uint32_t,
kNumArgsTestedPos,
kNumArgsTestedSize> {};
class TrackingExactnessBit : public BitField<uint32_t,
bool,
kTrackingExactnessPos,
kTrackingExactnessSize> {};
class DeoptReasonBits : public BitField<uint32_t,
uint32_t,
ICData::kDeoptReasonPos,

View file

@ -703,7 +703,7 @@ void ICData::Reset(Zone* zone) const {
RebindRule rule = rebind_rule();
if (rule == kInstance) {
const intptr_t num_args = NumArgsTested();
const bool tracking_exactness = IsTrackingExactness();
const bool tracking_exactness = is_tracking_exactness();
const intptr_t len = Length();
// We need at least one non-sentinel entry to require a check
// for the smi fast path case.

View file

@ -1701,10 +1701,8 @@ class RawICData : public RawObject {
RawArray* entries_; // Contains class-ids, target and count.
RawString* target_name_; // Name of target function.
RawArray* args_descriptor_; // Arguments descriptor.
// Static type of the receiver. If it is set then we are performing
// exactness profiling for the receiver type. See StaticTypeExactnessState
// class for more information.
NOT_IN_PRECOMPILED(RawAbstractType* static_receiver_type_);
// Static type of the receiver, if instance call and available.
NOT_IN_PRECOMPILED(RawAbstractType* receivers_static_type_);
RawObject* owner_; // Parent/calling function or original IC of cloned IC.
VISIT_TO(RawObject*, owner_);
RawObject** to_snapshot(Snapshot::Kind kind) {

View file

@ -1142,14 +1142,14 @@ static RawFunction* InlineCacheMissHandler(
return target_function.raw();
}
if (args.length() == 1) {
if (ic_data.IsTrackingExactness()) {
if (ic_data.is_tracking_exactness()) {
#if !defined(DART_PRECOMPILED_RUNTIME)
const auto& receiver = *args[0];
const auto state = receiver.IsNull()
? StaticTypeExactnessState::NotExact()
: StaticTypeExactnessState::Compute(
Type::Cast(AbstractType::Handle(
ic_data.StaticReceiverType())),
ic_data.receivers_static_type())),
receiver);
ic_data.AddReceiverCheck(
receiver.GetClassId(), target_function,