From a41d1b5813b4e463cff6876bb24730642ffa8405 Mon Sep 17 00:00:00 2001 From: Tess Strickland Date: Tue, 9 May 2023 14:53:47 +0000 Subject: [PATCH] [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 Reviewed-by: Martin Kustermann --- runtime/vm/type_testing_stubs.cc | 7 ++-- runtime/vm/type_testing_stubs_test.cc | 47 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc index 54969aba232..40c0cb0ea7f 100644 --- a/runtime/vm/type_testing_stubs.cc +++ b/runtime/vm/type_testing_stubs.cc @@ -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; diff --git a/runtime/vm/type_testing_stubs_test.cc b/runtime/vm/type_testing_stubs_test.cc index 347e31f330b..f54563be222 100644 --- a/runtime/vm/type_testing_stubs_test.cc +++ b/runtime/vm/type_testing_stubs_test.cc @@ -2224,6 +2224,10 @@ static const char* kLoadedScript = createAInt() => A(); createAString() => A(); + + (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(); createA2Int() => A2(); createA2String() => A2(); + + (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) {