Revert "[vm] Account for changes in the implicit getters due to load guards"
This reverts commit 87810072db06c1a16fdaa7bf6bb77ef4e6405af5.
Reason for revert: breaks hot reload test case in Flutter,
reverting as temporary workaround to https://github.com/flutter/flutter/issues/120091.
Original change's description:
> [vm] Account for changes in the implicit getters due to load guards
>
> Hot reload may mark certain fields as 'needs_load_guard', meaning
> types of their values should be checked on access, in the implicit
> getters.
>
> Unoptimized code for implicit getters of such fields should be
> forcefully recompiled during hot reload in order to update their
> code. This ensures that optimized code in future can deoptimize
> into the updated unoptimized code of implicit getters.
>
> TEST=vm/cc/IsolateReload_ImplicitGetterWithLoadGuard
> TEST=corelib/string_fromcharcodes_test, lib/convert/json_utf8_chunk_test
> and vm/dart/string_equals_test on vm-reload-linux-{debug,release}-x64
> configurations.
>
> Fixes https://github.com/dart-lang/sdk/issues/51215
> Fixes https://github.com/flutter/flutter/issues/103268
>
> Change-Id: I1afbeabb4a60b7a5f69ed055f40beeb6d3dcf359
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/280401
> Reviewed-by: Martin Kustermann <kustermann@google.com>
> Commit-Queue: Alexander Markov <alexmarkov@google.com>
> Reviewed-by: Ryan Macnak <rmacnak@google.com>
# Not skipping CQ checks because original CL landed > 1 day ago.
Change-Id: Ia7dba382547cceb38524707f9ecef892016108de
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/281220
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Zach Anderson <zra@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index f685e53..052bb73 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -3862,44 +3862,44 @@
}
}
body += NullConstant();
- } else {
- ASSERT(is_getter);
- if (is_method) {
- body += LoadLocal(parsed_function_->ParameterVariable(0));
- body += LoadField(
- field, /*calls_initializer=*/field.NeedsInitializationCheckOnLoad());
- } else if (field.is_const()) {
- const auto& value = Object::Handle(Z, field.StaticConstFieldValue());
- if (value.IsError()) {
- Report::LongJump(Error::Cast(value));
- }
- body += Constant(Instance::ZoneHandle(Z, Instance::RawCast(value.ptr())));
- } else {
- // Static fields
- // - with trivial initializer
- // - without initializer if they are not late
- // are initialized eagerly and do not have implicit getters.
- // Static fields with non-trivial initializer need getter to perform
- // lazy initialization. Late fields without initializer need getter
- // to make sure they are already initialized.
- ASSERT(field.has_nontrivial_initializer() ||
- (field.is_late() && !field.has_initializer()));
- body += LoadStaticField(field, /*calls_initializer=*/true);
- }
-
- if (is_method || !field.is_const()) {
+ } else if (is_getter && is_method) {
+ ASSERT(!field.needs_load_guard()
+ NOT_IN_PRODUCT(|| IG->HasAttemptedReload()));
+ body += LoadLocal(parsed_function_->ParameterVariable(0));
+ body += LoadField(
+ field, /*calls_initializer=*/field.NeedsInitializationCheckOnLoad());
+ if (field.needs_load_guard()) {
#if defined(PRODUCT)
- RELEASE_ASSERT(!field.needs_load_guard());
+ UNREACHABLE();
#else
- // Always build fragment for load guard to maintain stable deopt_id
- // numbering, but link it into the graph only if field actually
- // needs load guard.
- Fragment load_guard = CheckAssignable(
- AbstractType::Handle(Z, field.type()), Symbols::FunctionResult());
- if (field.needs_load_guard()) {
- ASSERT(IG->HasAttemptedReload());
- body += load_guard;
- }
+ body += CheckAssignable(AbstractType::Handle(Z, field.type()),
+ Symbols::FunctionResult());
+#endif
+ }
+ } else if (field.is_const()) {
+ const auto& value = Object::Handle(Z, field.StaticConstFieldValue());
+ if (value.IsError()) {
+ Report::LongJump(Error::Cast(value));
+ }
+ body += Constant(Instance::ZoneHandle(Z, Instance::RawCast(value.ptr())));
+ } else {
+ // Static fields
+ // - with trivial initializer
+ // - without initializer if they are not late
+ // are initialized eagerly and do not have implicit getters.
+ // Static fields with non-trivial initializer need getter to perform
+ // lazy initialization. Late fields without initializer need getter
+ // to make sure they are already initialized.
+ ASSERT(field.has_nontrivial_initializer() ||
+ (field.is_late() && !field.has_initializer()));
+ body += LoadStaticField(field, /*calls_initializer=*/true);
+ if (field.needs_load_guard()) {
+#if defined(PRODUCT)
+ UNREACHABLE();
+#else
+ ASSERT(IsolateGroup::Current()->HasAttemptedReload());
+ body += CheckAssignable(AbstractType::Handle(Z, field.type()),
+ Symbols::FunctionResult());
#endif
}
}
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 43e1de9..f902c84 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -2005,11 +2005,8 @@
InvalidateKernelInfos(zone, kernel_infos);
InvalidateSuspendStates(zone, suspend_states);
- InvalidateFields(zone, fields, instances);
-
- // After InvalidateFields in order to invalidate
- // implicit getters which need load guards.
InvalidateFunctions(zone, functions);
+ InvalidateFields(zone, fields, instances);
}
void ProgramReloadContext::InvalidateKernelInfos(
@@ -2054,7 +2051,6 @@
Class& owning_class = Class::Handle(zone);
Library& owning_lib = Library::Handle(zone);
Code& code = Code::Handle(zone);
- Field& field = Field::Handle(zone);
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
for (intptr_t i = 0; i < functions.length(); i++) {
const Function& func = *functions[i];
@@ -2066,20 +2062,9 @@
code = func.CurrentCode();
ASSERT(!code.IsNull());
- // Force recompilation of unoptimized code of implicit getters
- // in order to add load guards. This is needed for future
- // deoptimizations which will expect load guard in the unoptimized code.
- bool recompile_for_load_guard = false;
- if (func.IsImplicitGetterFunction() ||
- func.IsImplicitStaticGetterFunction()) {
- field = func.accessor_field();
- recompile_for_load_guard = field.needs_load_guard();
- }
-
owning_class = func.Owner();
owning_lib = owning_class.library();
- const bool clear_unoptimized_code =
- IsDirty(owning_lib) || recompile_for_load_guard;
+ const bool clear_code = IsDirty(owning_lib);
const bool stub_code = code.IsStubCode();
// Zero edge counters, before clearing the ICDataArray, since that's where
@@ -2088,7 +2073,7 @@
if (stub_code) {
// Nothing to reset.
- } else if (clear_unoptimized_code) {
+ } else if (clear_code) {
VTIR_Print("Marking %s for recompilation, clearing code\n",
func.ToCString());
// Null out the ICData array and code.
diff --git a/runtime/vm/isolate_reload_test.cc b/runtime/vm/isolate_reload_test.cc
index e09d4d2..91f6a64 100644
--- a/runtime/vm/isolate_reload_test.cc
+++ b/runtime/vm/isolate_reload_test.cc
@@ -5007,63 +5007,6 @@
EXPECT_STREQ("okay", SimpleInvokeStr(lib, "main"));
}
-// Regression test for https://github.com/dart-lang/sdk/issues/51215.
-TEST_CASE(IsolateReload_ImplicitGetterWithLoadGuard) {
- const char* kLibScript = R"(
- import 'file:///test:isolate_reload_helper';
-
- class A {
- int x;
- A(this.x);
- A.withUinitializedObject(int Function() callback) : x = callback();
- }
-
- A a = A(3);
-
- main() {
- int sum = 0;
- // Trigger OSR and optimize this function.
- for (int i = 0; i < 30000; ++i) {
- sum += i;
- }
- // Make sure A.get:x is compiled.
- int y = a.x;
- // Reload while having an uninitialized
- // object A on the stack. This should result in
- // a load guard for A.x.
- A.withUinitializedObject(() {
- reloadTest();
- return 4;
- });
- // Trigger OSR and optimize this function once again.
- for (int i = 0; i < 30000; ++i) {
- sum += i;
- }
- // Trigger deoptimization in A.get:x.
- // It should correctly deoptimize into an implicit
- // getter with a load guard.
- a.x = 0x8070605040302010;
- int z = a.x & 0xffff;
- return "y: $y, z: $z";
- }
- )";
-
- Dart_Handle lib1 =
- TestCase::LoadTestLibrary("test_lib1.dart", kLibScript, nullptr);
- EXPECT_VALID(lib1);
-
- const char* kMainScript = R"(
- main() {}
- )";
-
- // Trigger hot reload during execution of 'main' from test_lib1
- // without reloading test_lib1, so its unoptimized code is retained.
- EXPECT_VALID(TestCase::LoadTestScript(kMainScript, nullptr));
- EXPECT_VALID(TestCase::SetReloadTestScript(kMainScript));
-
- EXPECT_STREQ("y: 3, z: 8208", SimpleInvokeStr(lib1, "main"));
-}
-
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
} // namespace dart
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index 2b1e257..57286f7 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -284,18 +284,12 @@
GCTestHelper::CollectOldSpace();
}
-#endif // !PRODUCT
-
-static void LoadIsolateReloadTestLibIfNeeded(const char* script) {
-#ifndef PRODUCT
- if (strstr(script, IsolateReloadTestLibUri()) != nullptr) {
- Dart_Handle result = TestCase::LoadTestLibrary(
- IsolateReloadTestLibUri(), kIsolateReloadTestLibSource,
- IsolateReloadTestNativeResolver);
- EXPECT_VALID(result);
- }
-#endif // ifndef PRODUCT
+static Dart_Handle LoadIsolateReloadTestLib() {
+ return TestCase::LoadTestLibrary(IsolateReloadTestLibUri(),
+ kIsolateReloadTestLibSource,
+ IsolateReloadTestNativeResolver);
}
+#endif // !PRODUCT
char* TestCase::CompileTestScriptWithDFE(const char* url,
const char* source,
@@ -424,7 +418,12 @@
const char* lib_url,
bool finalize_classes,
bool allow_compile_errors) {
- LoadIsolateReloadTestLibIfNeeded(script);
+#ifndef PRODUCT
+ if (strstr(script, IsolateReloadTestLibUri()) != NULL) {
+ Dart_Handle result = LoadIsolateReloadTestLib();
+ EXPECT_VALID(result);
+ }
+#endif // ifndef PRODUCT
Dart_SourceFile* sourcefiles = NULL;
intptr_t num_sources = BuildSourceFilesArray(&sourcefiles, script, lib_url);
Dart_Handle result =
@@ -441,7 +440,6 @@
Dart_Handle TestCase::LoadTestLibrary(const char* lib_uri,
const char* script,
Dart_NativeEntryResolver resolver) {
- LoadIsolateReloadTestLibIfNeeded(script);
const char* prefixed_lib_uri =
OS::SCreate(Thread::Current()->zone(), "file:///%s", lib_uri);
Dart_SourceFile sourcefiles[] = {{prefixed_lib_uri, script}};