[vm] Clear out specialized stubs for record types on reload.

There's also no need to visit function types on reload, as their
TTSes do not specialize.

TEST=vm/cc/TTS_Reload

Change-Id: Ibbc7bc220b9b75aab27b3e6815be06db461ec46f
Cq-Include-Trybots: luci.dart.try:vm-reload-linux-debug-x64-try,vm-reload-linux-release-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-reload-rollback-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/302321
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Tess Strickland 2023-05-09 14:53:47 +00:00 committed by Commit Queue
parent 54be4bc80e
commit a41d1b5813
2 changed files with 52 additions and 2 deletions

View file

@ -183,6 +183,8 @@ CodePtr TypeTestingStubGenerator::OptimizedCodeForType(
}
if (type.IsCanonical()) {
// When adding any new types that can have specialized TTSes, also update
// CollectTypes::VisitObject appropriately.
if (type.IsType() || type.IsRecordType()) {
#if !defined(DART_PRECOMPILED_RUNTIME)
const Code& code =
@ -1524,8 +1526,9 @@ void DeoptimizeTypeTestingStubs() {
: zone_(zone), types_(types), cache_(SubtypeTestCache::Handle(zone)) {}
void VisitObject(ObjectPtr object) {
// Only types and function types may have optimized TTSes.
if (object->IsType() || object->IsFunctionType()) {
// Only types and record types may have optimized TTSes,
// see TypeTestingStubGenerator::OptimizedCodeForType.
if (object->IsType() || object->IsRecordType()) {
types_->Add(&AbstractType::CheckedHandle(zone_, object));
} else if (object->IsSubtypeTestCache()) {
cache_ ^= object;

View file

@ -2224,6 +2224,10 @@ static const char* kLoadedScript =
createAInt() => A<int>();
createAString() => A<String>();
(int, int) createRecordIntInt() => (1, 2);
(String, int) createRecordStringInt() => ("foo", 2);
(int, String) createRecordIntString() => (1, "bar");
)";
static const char* kReloadedScript =
@ -2235,9 +2239,15 @@ static const char* kReloadedScript =
createAString() => A<String>();
createA2Int() => A2<int>();
createA2String() => A2<String>();
(int, int) createRecordIntInt() => (1, 2);
(String, int) createRecordStringInt() => ("foo", 2);
(int, String) createRecordIntString() => (1, "bar");
)";
ISOLATE_UNIT_TEST_CASE(TTS_Reload) {
auto* const zone = thread->zone();
auto& root_library = Library::Handle(LoadTestScript(kLoadedScript));
const auto& class_a = Class::Handle(GetClass(root_library, "A"));
ClassFinalizer::FinalizeTypesInClass(class_a);
@ -2245,6 +2255,13 @@ ISOLATE_UNIT_TEST_CASE(TTS_Reload) {
const auto& aint = Object::Handle(Invoke(root_library, "createAInt"));
const auto& astring = Object::Handle(Invoke(root_library, "createAString"));
const auto& record_int_int =
Instance::CheckedHandle(zone, Invoke(root_library, "createRecordIntInt"));
const auto& record_int_string = Instance::CheckedHandle(
zone, Invoke(root_library, "createRecordIntString"));
const auto& record_string_int = Instance::CheckedHandle(
zone, Invoke(root_library, "createRecordStringInt"));
const auto& tav_null = Object::null_type_arguments();
const auto& tav_int =
TypeArguments::Handle(Instance::Cast(aint).GetTypeArguments());
@ -2255,10 +2272,28 @@ ISOLATE_UNIT_TEST_CASE(TTS_Reload) {
auto& type_a_int = Type::Handle(Type::New(class_a, tav_int));
FinalizeAndCanonicalize(&type_a_int);
auto& type_record_int_int =
AbstractType::Handle(record_int_int.GetType(Heap::kNew));
FinalizeAndCanonicalize(&type_record_int_int);
TTSTestState state(thread, type_a_int);
state.InvokeLazilySpecializedStub({aint, tav_null, tav_null});
state.InvokeExistingStub(Failure({astring, tav_null, tav_null}));
TTSTestState record_state(thread, type_record_int_int);
record_state.InvokeLazilySpecializedStub(
{record_int_int, tav_null, tav_null});
record_state.InvokeExistingStub(
Failure({record_string_int, tav_null, tav_null}));
record_state.InvokeExistingStub(
Failure({record_int_string, tav_null, tav_null}));
// Make sure the stubs are specialized prior to reload.
EXPECT(type_a_int.type_test_stub() !=
TypeTestingStubGenerator::DefaultCodeForType(type_a_int));
EXPECT(type_record_int_int.type_test_stub() !=
TypeTestingStubGenerator::DefaultCodeForType(type_record_int_int));
root_library = ReloadTestScript(kReloadedScript);
const auto& a2int = Object::Handle(Invoke(root_library, "createA2Int"));
const auto& a2string = Object::Handle(Invoke(root_library, "createA2String"));
@ -2267,15 +2302,27 @@ ISOLATE_UNIT_TEST_CASE(TTS_Reload) {
// default stub for that type.
EXPECT(type_a_int.type_test_stub() ==
TypeTestingStubGenerator::DefaultCodeForType(type_a_int));
EXPECT(type_record_int_int.type_test_stub() ==
TypeTestingStubGenerator::DefaultCodeForType(type_record_int_int));
// Reloading either removes or resets the type testing cache.
EXPECT(state.current_stc() == SubtypeTestCache::null() ||
(state.current_stc() == state.last_stc().ptr() &&
state.last_stc().NumberOfChecks() == 0));
EXPECT(record_state.current_stc() == SubtypeTestCache::null() ||
(record_state.current_stc() == record_state.last_stc().ptr() &&
record_state.last_stc().NumberOfChecks() == 0));
state.InvokeExistingStub(Respecialization({aint, tav_null, tav_null}));
state.InvokeExistingStub(Failure({astring, tav_null, tav_null}));
state.InvokeExistingStub({a2int, tav_null, tav_null});
state.InvokeExistingStub(Failure({a2string, tav_null, tav_null}));
record_state.InvokeExistingStub(
Respecialization({record_int_int, tav_null, tav_null}));
record_state.InvokeExistingStub(
Failure({record_string_int, tav_null, tav_null}));
record_state.InvokeExistingStub(
Failure({record_int_string, tav_null, tav_null}));
}
ISOLATE_UNIT_TEST_CASE(TTS_Partial_Reload) {