| Index: runtime/vm/code_generator.cc
|
| diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc
|
| index d78098198b10086df9aa17ddee4a4546caa6cc89..51e70e8a0bee2ff314c1267803ffcaf20f8c51f3 100644
|
| --- a/runtime/vm/code_generator.cc
|
| +++ b/runtime/vm/code_generator.cc
|
| @@ -1026,6 +1026,121 @@ DEFINE_RUNTIME_ENTRY(StaticCallMissHandlerTwoArgs, 3) {
|
| }
|
|
|
|
|
| +#if !defined(TARGET_ARCH_DBC)
|
| +static bool IsSingleTarget(Isolate* isolate,
|
| + Zone* zone,
|
| + intptr_t lower_cid,
|
| + intptr_t upper_cid,
|
| + const Function& target,
|
| + const String& name) {
|
| + Class& cls = Class::Handle(zone);
|
| + ClassTable* table = isolate->class_table();
|
| + Function& other_target = Function::Handle(zone);
|
| + for (intptr_t cid = lower_cid; cid <= upper_cid; cid++) {
|
| + if (!table->HasValidClassAt(cid)) continue;
|
| + cls = table->At(cid);
|
| + if (cls.is_abstract()) continue;
|
| + if (!cls.is_allocated()) continue;
|
| + other_target = Resolver::ResolveDynamicAnyArgs(zone, cls, name,
|
| + false /* allow_add */);
|
| + if (other_target.raw() != target.raw()) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +#endif
|
| +
|
| +
|
| +// Handle a miss of a single target cache.
|
| +// Arg0: Receiver.
|
| +// Returns: the ICData used to continue with a polymorphic call.
|
| +DEFINE_RUNTIME_ENTRY(SingleTargetMiss, 1) {
|
| +#if defined(TARGET_ARCH_DBC)
|
| + // DBC does not use switchable calls.
|
| + UNREACHABLE();
|
| +#else
|
| + const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
|
| +
|
| + DartFrameIterator iterator;
|
| + StackFrame* caller_frame = iterator.NextFrame();
|
| + ASSERT(caller_frame->IsDartFrame());
|
| + const Code& caller_code = Code::Handle(zone, caller_frame->LookupDartCode());
|
| + const Function& caller_function =
|
| + Function::Handle(zone, caller_frame->LookupDartFunction());
|
| +
|
| + SingleTargetCache& cache = SingleTargetCache::Handle(zone);
|
| + cache ^= CodePatcher::GetSwitchableCallDataAt(caller_frame->pc(),
|
| + caller_code);
|
| + Code& old_target_code = Code::Handle(zone, cache.target());
|
| + Function& old_target = Function::Handle(zone);
|
| + old_target ^= old_target_code.owner();
|
| +
|
| + // We lost the original ICData when we patched to the monomorphic case.
|
| + const String& name = String::Handle(zone, old_target.name());
|
| + ASSERT(!old_target.HasOptionalParameters());
|
| + const Array& descriptor = Array::Handle(zone,
|
| + ArgumentsDescriptor::New(old_target.num_fixed_parameters()));
|
| + const ICData& ic_data =
|
| + ICData::Handle(zone, ICData::New(caller_function,
|
| + name,
|
| + descriptor,
|
| + Thread::kNoDeoptId,
|
| + 1, /* args_tested */
|
| + false /* static_call */));
|
| +
|
| + // Maybe add the new target.
|
| + Class& cls = Class::Handle(zone, receiver.clazz());
|
| + ArgumentsDescriptor args_desc(descriptor);
|
| + Function& target_function = Function::Handle(zone,
|
| + Resolver::ResolveDynamicForReceiverClass(cls,
|
| + name,
|
| + args_desc));
|
| + if (target_function.IsNull()) {
|
| + target_function = InlineCacheMissHelper(receiver, descriptor, name);
|
| + }
|
| + if (target_function.IsNull()) {
|
| + ASSERT(!FLAG_lazy_dispatchers);
|
| + } else {
|
| + ic_data.AddReceiverCheck(receiver.GetClassId(), target_function);
|
| + }
|
| +
|
| + if (old_target.raw() == target_function.raw()) {
|
| + intptr_t lower, upper;
|
| + if (receiver.GetClassId() < cache.lower_limit()) {
|
| + lower = receiver.GetClassId();
|
| + upper = cache.upper_limit();
|
| + } else {
|
| + lower = cache.lower_limit();
|
| + upper = receiver.GetClassId();
|
| + }
|
| +
|
| + if (IsSingleTarget(isolate, zone, lower, upper, target_function, name)) {
|
| + cache.set_lower_limit(lower);
|
| + cache.set_upper_limit(upper);
|
| + // Return the ICData. The single target stub will jump to continue in the
|
| + // IC call stub.
|
| + arguments.SetReturn(ic_data);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + // Call site is not single target, switch to call using ICData.
|
| + const Code& stub =
|
| + Code::Handle(zone, StubCode::ICCallThroughCode_entry()->code());
|
| + ASSERT(!Isolate::Current()->compilation_allowed());
|
| + CodePatcher::PatchSwitchableCallAt(caller_frame->pc(),
|
| + caller_code,
|
| + ic_data,
|
| + stub);
|
| +
|
| + // Return the ICData. The single target stub will jump to continue in the
|
| + // IC call stub.
|
| + arguments.SetReturn(ic_data);
|
| +#endif
|
| +}
|
| +
|
| +
|
| // Handle a miss of a megamorphic cache.
|
| // Arg0: Receiver.
|
| // Returns: the ICData used to continue with a polymorphic call.
|
| @@ -1084,6 +1199,37 @@ DEFINE_RUNTIME_ENTRY(MonomorphicMiss, 1) {
|
| ic_data.AddReceiverCheck(receiver.GetClassId(), target_function);
|
| }
|
|
|
| + if (old_target.raw() == target_function.raw()) {
|
| + intptr_t lower, upper;
|
| + if (old_expected_cid.Value() < receiver.GetClassId()) {
|
| + lower = old_expected_cid.Value();
|
| + upper = receiver.GetClassId();
|
| + } else {
|
| + lower = receiver.GetClassId();
|
| + upper = old_expected_cid.Value();
|
| + }
|
| +
|
| + if (IsSingleTarget(isolate, zone, lower, upper, target_function, name)) {
|
| + const SingleTargetCache& cache =
|
| + SingleTargetCache::Handle(SingleTargetCache::New());
|
| + const Code& code = Code::Handle(target_function.CurrentCode());
|
| + cache.set_target(code);
|
| + cache.set_entry_point(code.UncheckedEntryPoint());
|
| + cache.set_lower_limit(lower);
|
| + cache.set_upper_limit(upper);
|
| + const Code& stub = Code::Handle(zone,
|
| + StubCode::SingleTargetCall_entry()->code());
|
| + CodePatcher::PatchSwitchableCallAt(caller_frame->pc(),
|
| + caller_code,
|
| + cache,
|
| + stub);
|
| + // Return the ICData. The miss stub will jump to continue in the IC call
|
| + // stub.
|
| + arguments.SetReturn(ic_data);
|
| + return;
|
| + }
|
| + }
|
| +
|
| // Patch to call through stub.
|
| const Code& stub =
|
| Code::Handle(zone, StubCode::ICCallThroughCode_entry()->code());
|
|
|