[vm] Ensure a switchable callsite never looses knowledge of it being dyn:* during transitions

Issue https://github.com/dart-lang/sdk/issues/42517

Change-Id: I6fb414740b59f7f710fba10c27c126997a4346b6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152847
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann
2020-07-01 19:53:34 +00:00
committed by commit-bot@chromium.org
parent fffa2bd30c
commit fdeb01897f
7 changed files with 271 additions and 200 deletions
@@ -0,0 +1,31 @@
// Copyright (c) 2020, 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.
import 'package:expect/expect.dart';
final dynamic l = <dynamic>[A(), B()];
main() {
// Switchable call site goes from UnlinkedCall -> Monomorphic.
Expect.equals('C(42)', bar(0, 42));
// Switchable call site goes from Monomorphic -> Polymorphic.
// It has to retain the fact that call site is dyn:*.
Expect.throwsTypeError(() => bar(1, 'a'));
Expect.equals('B(43)', bar(1, 42));
}
@pragma('vm:never-inline')
bar(int j, dynamic arg) => l[j].foo(arg);
class A {
// This will not get a dyn:* forwarder because it's parameter type is
// top-type.
String foo(Object? a) => 'C($a)';
}
class B {
// This will get a dyn:* forwarder because it's parameter type is not
// top-type (and neither covariant)
String foo(int a) => 'B(${a + 1})';
}
@@ -0,0 +1,37 @@
// Copyright (c) 2020, 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.
import 'package:expect/expect.dart';
final dynamic l = <dynamic>[A(), A1(), B()];
main() {
// Switchable call site goes from UnlinkedCall -> Monomorphic.
Expect.equals('C(42)', bar(0, 42));
// Switchable call site goes from Monomorphic -> SingleTarget.
Expect.equals('C(42)', bar(1, 42));
// Switchable call site goes from SingleTarget -> Polymorphic.
// It has to retain the fact that call site is dyn:*.
Expect.throwsTypeError(() => bar(2, 'a'));
Expect.equals('B(43)', bar(2, 42));
}
@pragma('vm:never-inline')
bar(int j, dynamic arg) => l[j].foo(arg);
class A {
// This will not get a dyn:* forwarder because it's parameter type is
// top-type.
String foo(Object? a) => 'C($a)';
}
class A1 extends A {
// A different receiver cid but with same target (i.e. we do not override A).
}
class B {
// This will get a dyn:* forwarder because it's parameter type is not
// top-type (and neither covariant)
String foo(int a) => 'B(${a + 1})';
}
@@ -0,0 +1,31 @@
// Copyright (c) 2020, 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.
import 'package:expect/expect.dart';
final dynamic l = <dynamic>[A(), B()];
main() {
// Switchable call site goes from UnlinkedCall -> Monomorphic.
Expect.equals('C(42)', bar(0, 42));
// Switchable call site goes from Monomorphic -> Polymorphic.
// It has to retain the fact that call site is dyn:*.
Expect.throwsTypeError(() => bar(1, 'a'));
Expect.equals('B(43)', bar(1, 42));
}
@pragma('vm:never-inline')
bar(int j, dynamic arg) => l[j].foo(arg);
class A {
// This will not get a dyn:* forwarder because it's parameter type is
// top-type.
String foo(Object a) => 'C($a)';
}
class B {
// This will get a dyn:* forwarder because it's parameter type is not
// top-type (and neither covariant)
String foo(int a) => 'B(${a + 1})';
}
@@ -0,0 +1,37 @@
// Copyright (c) 2020, 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.
import 'package:expect/expect.dart';
final dynamic l = <dynamic>[A(), A1(), B()];
main() {
// Switchable call site goes from UnlinkedCall -> Monomorphic.
Expect.equals('C(42)', bar(0, 42));
// Switchable call site goes from Monomorphic -> SingleTarget.
Expect.equals('C(42)', bar(1, 42));
// Switchable call site goes from SingleTarget -> Polymorphic.
// It has to retain the fact that call site is dyn:*.
Expect.throwsTypeError(() => bar(2, 'a'));
Expect.equals('B(43)', bar(2, 42));
}
@pragma('vm:never-inline')
bar(int j, dynamic arg) => l[j].foo(arg);
class A {
// This will not get a dyn:* forwarder because it's parameter type is
// top-type.
String foo(Object a) => 'C($a)';
}
class A1 extends A {
// A different receiver cid but with same target (i.e. we do not override A).
}
class B {
// This will get a dyn:* forwarder because it's parameter type is not
// top-type (and neither covariant)
String foo(int a) => 'B(${a + 1})';
}
+8
View File
@@ -370,6 +370,10 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
}
Mutex* subtype_test_cache_mutex() { return &subtype_test_cache_mutex_; }
#if defined(DART_PRECOMPILED_RUNTIME)
Mutex* unlinked_call_map_mutex() { return &unlinked_call_map_mutex_; }
#endif
#if !defined(DART_PRECOMPILED_RUNTIME)
Mutex* initializer_functions_mutex() { return &initializer_functions_mutex_; }
#endif // !defined(DART_PRECOMPILED_RUNTIME)
@@ -646,6 +650,10 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
Mutex type_arguments_canonicalization_mutex_;
Mutex subtype_test_cache_mutex_;
#if defined(DART_PRECOMPILED_RUNTIME)
Mutex unlinked_call_map_mutex_;
#endif
#if !defined(DART_PRECOMPILED_RUNTIME)
Mutex initializer_functions_mutex_;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
+19
View File
@@ -1274,6 +1274,25 @@ void ProgramVisitor::Dedup(Thread* thread) {
// Reduces binary size but obfuscates profiler results.
if (FLAG_dedup_instructions) {
// In non-bare mode (unused atm) dedupping instructions would cause us to
// loose the ability to uniquely map a PC to a given UnlinkedCall object,
// since two code objects might point to the same deduped instructions
// object but might have two different UnlinkedCall objects in their pool.
//
// In bare mode this cannot happen because different UnlinkedCall objects
// would get different indices into the (global) object pool, therefore
// making the instructions different.
//
// (When transitioning the switchable call site we loose track of the args
// descriptor. Since we need it for further transitions we currently save it
// via a PC -> UnlinkedCall mapping).
//
// We therfore disable the instruction deduplication in product-non-bare
// mode (which is unused atm).
#if defined(PRODUCT)
if (FLAG_precompiled_mode && !FLAG_use_bare_instructions) return;
#endif
DedupInstructions(zone, isolate);
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
+108 -200
View File
@@ -1478,6 +1478,8 @@ static bool IsSingleTarget(Isolate* isolate,
return true;
}
#if defined(DART_PRECOMPILED_RUNTIME)
class SavedUnlinkedCallMapKeyEqualsTraits : public AllStatic {
public:
static const char* Name() { return "SavedUnlinkedCallMapKeyEqualsTraits "; }
@@ -1499,6 +1501,8 @@ static void SaveUnlinkedCall(Zone* zone,
uword frame_pc,
const UnlinkedCall& unlinked_call) {
IsolateGroup* isolate_group = isolate->group();
SafepointMutexLocker ml(isolate_group->unlinked_call_map_mutex());
if (isolate_group->saved_unlinked_calls() == Array::null()) {
const auto& initial_map =
Array::Handle(zone, HashTables::New<UnlinkedCallMap>(16, Heap::kOld));
@@ -1510,37 +1514,44 @@ static void SaveUnlinkedCall(Zone* zone,
const auto& pc = Integer::Handle(zone, Integer::NewFromUint64(frame_pc));
// Some other isolate might have updated unlinked_call_map[pc] too, but
// their update should be identical to ours.
UnlinkedCall& new_or_old_value = UnlinkedCall::Handle(
const auto& new_or_old_value = UnlinkedCall::Handle(
zone, UnlinkedCall::RawCast(
unlinked_call_map.InsertOrGetValue(pc, unlinked_call)));
RELEASE_ASSERT(new_or_old_value.raw() == unlinked_call.raw());
isolate_group->set_saved_unlinked_calls(unlinked_call_map.Release());
}
#if defined(DART_PRECOMPILED_RUNTIME)
static UnlinkedCallPtr LoadUnlinkedCall(Zone* zone,
Isolate* isolate,
uword pc,
bool is_monomorphic_hit) {
uword pc) {
IsolateGroup* isolate_group = isolate->group();
ASSERT(isolate_group->saved_unlinked_calls() != Array::null());
SafepointMutexLocker ml(isolate_group->unlinked_call_map_mutex());
ASSERT(isolate_group->saved_unlinked_calls() != Array::null());
UnlinkedCallMap unlinked_call_map(zone,
isolate_group->saved_unlinked_calls());
const auto& pc_integer = Integer::Handle(zone, Integer::NewFromUint64(pc));
const auto& unlinked_call = UnlinkedCall::Cast(
Object::Handle(zone, unlinked_call_map.GetOrDie(pc_integer)));
// Only remove entry from unlinked_call_map if we are actually transitioning
// out of monomorphic state.
if (!is_monomorphic_hit) {
unlinked_call_map.Remove(pc_integer);
isolate_group->set_saved_unlinked_calls(unlinked_call_map.Release());
}
isolate_group->set_saved_unlinked_calls(unlinked_call_map.Release());
return unlinked_call.raw();
}
#endif
// NOTE: Right now we never delete [UnlinkedCall] objects. They are needed while
// a call site is in Unlinked/Monomorphic/MonomorphicSmiable/SingleTarget
// states.
//
// Theoretically we could free the [UnlinkedCall] object once we transition the
// call site to use ICData/MegamorphicCache, but that would require careful
// coordination between the deleter and a possible concurrent reader.
//
// To simplify the code we decided not to do that atm (only a very small
// fraction of callsites in AOT use switchable calls, the name/args-descriptor
// objects are kept alive anyways -> there is little memory savings from
// freeing the [UnlinkedCall] objects).
#endif // defined(DART_PRECOMPILED_RUNTIME)
class SwitchableCallHandler {
public:
@@ -1557,7 +1568,9 @@ class SwitchableCallHandler {
arguments_(arguments),
caller_frame_(caller_frame),
caller_code_(caller_code),
caller_function_(caller_function) {}
caller_function_(caller_function),
name_(String::Handle()),
args_descriptor_(Array::Handle()) {}
FunctionPtr ResolveTargetFunction(const Object& data);
void HandleMiss(const Object& old_data,
@@ -1572,16 +1585,11 @@ class SwitchableCallHandler {
const Function& target_function,
intptr_t* lower,
intptr_t* upper);
FunctionPtr LookupMonomorphicOldTargetNameDescriptorCid(
const Object& data,
String* out_name,
Array* out_descriptor,
classid_t* out_old_expected_cid,
bool keep_unlinked_call_map_entry_regardless,
bool* out_is_monomorphic_hit);
void DoMonomorphicMiss(const Object& data, const Function& target_function);
#if defined(DART_PRECOMPILED_RUNTIME)
void DoSingleTargetMiss(const SingleTargetCache& data,
const Function& target_function);
#endif // !defined(DART_PRECOMPILED_RUNTIME)
void DoICDataMiss(const ICData& data, const Function& target_function);
void DoMegamorphicMiss(const MegamorphicCache& data,
const Function& target_function);
@@ -1594,6 +1602,11 @@ class SwitchableCallHandler {
StackFrame* caller_frame_;
const Code& caller_code_;
const Function& caller_function_;
// Call-site information populated during resolution.
String& name_;
Array& args_descriptor_;
bool is_monomorphic_hit_ = false;
};
void SwitchableCallHandler::DoUnlinkedCall(const UnlinkedCall& unlinked,
@@ -1608,36 +1621,6 @@ void SwitchableCallHandler::DoUnlinkedCall(const UnlinkedCall& unlinked,
ic_data.AddReceiverCheck(receiver_.GetClassId(), target_function);
}
// In AOT bare mode, the PC -> Code mapping is ambiguous, since multiple code
// objects can have the same deduped instructions and bare frames are compact
// (i.e. have only PC but no code object in the frame)
//
// In JIT and AOT non-bare mode, instructions will push the unique code
// object on the frame.
//
// If we can find the unique code object of the callee, we can find it's
// owner function. If the callee function is non-generic and has no optional
// parameters, we can and thereby deduce the call-site argument descriptor +
// name from it.
//
// If not, we'll save the unlinked call object in a map.
//
// See [DoMonomorphicMiss]
const bool need_saved_unlinked_call =
(FLAG_use_bare_instructions && FLAG_dedup_instructions);
// We transition from an unlinked call to a monomorphic call. This transition
// will cause us to loose the argument descriptor information on the call
// site.
//
// Though if the monomorphic call site transitions to a
// polymorphic/megamorphic we need to reconstruct the arguments descriptor.
//
// We assume here that generated code never moves.
if (need_saved_unlinked_call) {
SaveUnlinkedCall(zone_, isolate_, caller_frame_->pc(), unlinked);
}
Object& object = Object::Handle(zone_, ic_data.raw());
Code& code = Code::Handle(zone_, StubCode::ICCallThroughCode().raw());
// If the target function has optional parameters or is generic, it's
@@ -1739,90 +1722,26 @@ static FunctionPtr Resolve(Zone* zone,
return target_function.raw();
}
FunctionPtr SwitchableCallHandler::LookupMonomorphicOldTargetNameDescriptorCid(
const Object& data,
String* out_name,
Array* out_descriptor,
classid_t* out_old_expected_cid,
bool keep_unlinked_call_map_entry_regardless,
bool* out_is_monomorphic_hit) {
#if defined(DART_PRECOMPILED_RUNTIME)
ASSERT(out_name != nullptr);
ASSERT(out_descriptor != nullptr);
ASSERT(out_old_expected_cid != nullptr);
ASSERT(out_is_monomorphic_hit != nullptr);
Function& old_target = Function::Handle(zone_);
if (data.IsSmi()) {
*out_old_expected_cid = Smi::Cast(data).Value();
} else if (data.IsMonomorphicSmiableCall()) {
*out_old_expected_cid = MonomorphicSmiableCall::Cast(data).expected_cid();
old_target ^=
Code::Handle(zone_, MonomorphicSmiableCall::Cast(data).target())
.owner();
} else {
UNREACHABLE();
}
// The site might have just been updated to monomorphic state with same
// exact class id, in which case we are staying in monomorphic state.
*out_is_monomorphic_hit = *out_old_expected_cid == receiver_.GetClassId();
if (FLAG_use_bare_instructions && FLAG_dedup_instructions) {
const UnlinkedCall& unlinked_call = UnlinkedCall::Handle(
zone_, LoadUnlinkedCall(zone_, isolate_, caller_frame_->pc(),
keep_unlinked_call_map_entry_regardless ||
*out_is_monomorphic_hit));
*out_name = unlinked_call.target_name();
*out_descriptor = unlinked_call.args_descriptor();
const Class& old_receiver_class = Class::Handle(
zone_, isolate_->class_table()->At(*out_old_expected_cid));
return Resolve(zone_, old_receiver_class, *out_name, *out_descriptor);
}
// We lost the original UnlinkedCall (and the name + arg descriptor inside
// it) when the call site transitioned from unlinked to monomorphic.
//
// Though we can deduce name + arg descriptor based on the first
// monomorphic callee (we are guaranteed it is not generic and does not have
// optional parameters, see DEFINE_RUNTIME_ENTRY(UnlinkedCall) above).
if (old_target.IsNull()) {
const Code& old_target_code =
Code::Handle(zone_, CodePatcher::GetSwitchableCallTargetAt(
caller_frame_->pc(), caller_code_));
old_target ^= old_target_code.owner();
}
const int kTypeArgsLen = 0;
*out_name = old_target.name();
// TODO(dartbug.com/33549): Update this code to use the size of the
// parameters when supporting calls to non-static methods with
// unboxed parameters.
*out_descriptor = ArgumentsDescriptor::NewBoxed(
kTypeArgsLen, old_target.num_fixed_parameters());
return old_target.raw();
#else
UNREACHABLE();
#endif
}
void SwitchableCallHandler::DoMonomorphicMiss(const Object& data,
const Function& target_function) {
#if defined(DART_PRECOMPILED_RUNTIME)
String& name = String::Handle(zone_);
Array& descriptor = Array::Handle(zone_);
bool is_monomorphic_hit;
classid_t old_expected_cid;
const Function& old_target = Function::Handle(
zone_, LookupMonomorphicOldTargetNameDescriptorCid(
data, &name, &descriptor, &old_expected_cid,
/*keep_unlinked_call_map_entry_regardless=*/false,
&is_monomorphic_hit));
if (data.IsSmi()) {
old_expected_cid = Smi::Cast(data).Value();
} else {
RELEASE_ASSERT(data.IsMonomorphicSmiableCall());
old_expected_cid = MonomorphicSmiableCall::Cast(data).expected_cid();
}
const bool is_monomorphic_hit = old_expected_cid == receiver_.GetClassId();
const auto& old_receiver_class =
Class::Handle(zone_, isolate_->class_table()->At(old_expected_cid));
const auto& old_target = Function::Handle(
zone_, Resolve(zone_, old_receiver_class, name_, args_descriptor_));
const ICData& ic_data =
ICData::Handle(zone_, ICData::New(caller_function_, name, descriptor,
DeoptId::kNone, 1, /* args_tested */
ICData::kInstance));
const ICData& ic_data = ICData::Handle(
zone_, ICData::New(caller_function_, name_, args_descriptor_,
DeoptId::kNone, 1, /* args_tested */
ICData::kInstance));
// Add the first target.
if (!old_target.IsNull()) {
ic_data.AddReceiverCheck(old_expected_cid, old_target);
@@ -1838,7 +1757,7 @@ void SwitchableCallHandler::DoMonomorphicMiss(const Object& data,
intptr_t lower = old_expected_cid;
intptr_t upper = old_expected_cid;
if (CanExtendSingleTargetRange(name, old_target, target_function, &lower,
if (CanExtendSingleTargetRange(name_, old_target, target_function, &lower,
&upper)) {
const SingleTargetCache& cache =
SingleTargetCache::Handle(zone_, SingleTargetCache::New());
@@ -1900,6 +1819,7 @@ void SwitchableCallHandler::DoMonomorphicMiss(const Object& data,
#endif // defined(DART_PRECOMPILED_RUNTIME)
}
#if defined(DART_PRECOMPILED_RUNTIME)
void SwitchableCallHandler::DoSingleTargetMiss(
const SingleTargetCache& data,
const Function& target_function) {
@@ -1908,24 +1828,17 @@ void SwitchableCallHandler::DoSingleTargetMiss(
Function::Handle(zone_, Function::RawCast(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());
ASSERT(!old_target.IsGeneric());
const int kTypeArgsLen = 0;
const Array& descriptor = Array::Handle(
zone_, ArgumentsDescriptor::NewBoxed(kTypeArgsLen,
old_target.num_fixed_parameters()));
const ICData& ic_data =
ICData::Handle(zone_, ICData::New(caller_function_, name, descriptor,
DeoptId::kNone, 1, /* args_tested */
ICData::kInstance));
const ICData& ic_data = ICData::Handle(
zone_, ICData::New(caller_function_, name_, args_descriptor_,
DeoptId::kNone, 1, /* args_tested */
ICData::kInstance));
if (!target_function.IsNull()) {
ic_data.AddReceiverCheck(receiver_.GetClassId(), target_function);
}
intptr_t lower = data.lower_limit();
intptr_t upper = data.upper_limit();
if (CanExtendSingleTargetRange(name, old_target, target_function, &lower,
if (CanExtendSingleTargetRange(name_, old_target, target_function, &lower,
&upper)) {
data.set_lower_limit(lower);
data.set_upper_limit(upper);
@@ -1946,6 +1859,7 @@ void SwitchableCallHandler::DoSingleTargetMiss(
arguments_.SetArgAt(0, stub);
arguments_.SetReturn(ic_data);
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
void SwitchableCallHandler::DoICDataMiss(const ICData& ic_data,
const Function& target_function) {
@@ -2036,76 +1950,68 @@ void SwitchableCallHandler::DoMegamorphicMiss(const MegamorphicCache& data,
}
FunctionPtr SwitchableCallHandler::ResolveTargetFunction(const Object& data) {
const Class& cls = Class::Handle(zone_, receiver_.clazz());
switch (data.GetClassId()) {
case kUnlinkedCallCid: {
const UnlinkedCall& unlinked = UnlinkedCall::Cast(data);
const String& name = String::Handle(zone_, unlinked.target_name());
const Array& descriptor =
Array::Handle(zone_, unlinked.args_descriptor());
return Resolve(zone_, cls, name, descriptor);
const auto& unlinked_call = UnlinkedCall::Cast(data);
#if defined(DART_PRECOMPILED_RUNTIME)
// When transitioning out of UnlinkedCall to other states (e.g.
// Monomorphic, MonomorphicSmiable, SingleTarget) we lose
// name/arg-descriptor in AOT mode and cannot recover it.
//
// Even if we could recover an old target function (which was missed) -
// which we cannot in AOT bare mode - we can still lose the name due to a
// dyn:* call site potentially targeting non-dyn:* targets.
//
// => We will therefore retain the unlinked call here.
//
// In JIT mode we always use ICData from the call site, which has the
// correct name/args-descriptor.
SaveUnlinkedCall(zone_, isolate_, caller_frame_->pc(), unlinked_call);
#endif // defined(DART_PRECOMPILED_RUNTIME)
name_ = unlinked_call.target_name();
args_descriptor_ = unlinked_call.args_descriptor();
break;
}
case kMonomorphicSmiableCallCid:
FALL_THROUGH;
#if defined(DART_PRECOMPILED_RUNTIME)
case kSmiCid: {
String& name = String::Handle(zone_);
Array& descriptor = Array::Handle(zone_);
classid_t old_expected_cid;
bool is_monomorphic_hit;
LookupMonomorphicOldTargetNameDescriptorCid(
data, &name, &descriptor, &old_expected_cid,
/*keep_unlinked_call_map_entry=*/true, &is_monomorphic_hit);
return Resolve(zone_, cls, name, descriptor);
case kSmiCid:
FALL_THROUGH;
case kSingleTargetCacheCid: {
const auto& unlinked_call = UnlinkedCall::Handle(
zone_, LoadUnlinkedCall(zone_, isolate_, caller_frame_->pc()));
name_ = unlinked_call.target_name();
args_descriptor_ = unlinked_call.args_descriptor();
break;
}
#else // JIT
case kArrayCid:
#else
case kArrayCid: {
// ICData three-element array: Smi(receiver CID), Smi(count),
// Function(target). It is the Array from ICData::entries_.
{
const ICData& ic_data = ICData::Handle(
zone_, FindICDataForInstanceCall(zone_, caller_code_,
caller_frame_->pc()));
RELEASE_ASSERT(!ic_data.IsNull());
const String& name = String::Handle(zone_, ic_data.target_name());
ASSERT(name.IsSymbol());
const Array& descriptor =
Array::CheckedHandle(zone_, ic_data.arguments_descriptor());
return Resolve(zone_, cls, name, descriptor);
}
#endif
case kSingleTargetCacheCid: {
const SingleTargetCache& single_target_cache =
SingleTargetCache::Cast(data);
const Code& old_target_code =
Code::Handle(zone_, single_target_cache.target());
const Function& old_target =
Function::Handle(zone_, Function::RawCast(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());
ASSERT(!old_target.IsGeneric());
const int kTypeArgsLen = 0;
const Array& descriptor = Array::Handle(
zone_, ArgumentsDescriptor::NewBoxed(
kTypeArgsLen, old_target.num_fixed_parameters()));
return Resolve(zone_, cls, name, descriptor);
const auto& ic_data = ICData::Handle(
zone_,
FindICDataForInstanceCall(zone_, caller_code_, caller_frame_->pc()));
RELEASE_ASSERT(!ic_data.IsNull());
name_ = ic_data.target_name();
args_descriptor_ = ic_data.arguments_descriptor();
break;
}
#endif // defined(DART_PRECOMPILED_RUNTIME)
case kICDataCid:
FALL_THROUGH;
case kMegamorphicCacheCid: {
const CallSiteData& call_site_data = CallSiteData::Cast(data);
const String& name = String::Handle(zone_, call_site_data.target_name());
const Array& descriptor =
Array::CheckedHandle(zone_, call_site_data.arguments_descriptor());
return Resolve(zone_, cls, name, descriptor);
name_ = call_site_data.target_name();
args_descriptor_ = call_site_data.arguments_descriptor();
break;
}
default:
UNREACHABLE();
}
const Class& cls = Class::Handle(zone_, receiver_.clazz());
return Resolve(zone_, cls, name_, args_descriptor_);
}
void SwitchableCallHandler::HandleMiss(const Object& old_data,
@@ -2121,17 +2027,19 @@ void SwitchableCallHandler::HandleMiss(const Object& old_data,
FALL_THROUGH;
#if defined(DART_PRECOMPILED_RUNTIME)
case kSmiCid:
#else // JIT
case kArrayCid:
// ICData three-element array: Smi(receiver CID), Smi(count),
// Function(target). It is the Array from ICData::entries_.
#endif
DoMonomorphicMiss(old_data, target_function);
break;
case kSingleTargetCacheCid:
ASSERT(old_code.raw() == StubCode::SingleTargetCall().raw());
DoSingleTargetMiss(SingleTargetCache::Cast(old_data), target_function);
break;
#else
case kArrayCid:
// ICData three-element array: Smi(receiver CID), Smi(count),
// Function(target). It is the Array from ICData::entries_.
DoMonomorphicMiss(old_data, target_function);
break;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
case kICDataCid:
ASSERT(old_code.raw() == StubCode::ICCallThroughCode().raw());
DoICDataMiss(ICData::Cast(old_data), target_function);