[vm] Use a weak set to represent dependent code.
Fixes a scaling limitation where compiling N functions using the same guarded field or CHA guarded interface will result in O(N^2) comparisons when registering the dependencies.
Don't use Instructions' address as the hash, as this gets relocated between the snapshot writer and reader.
TEST=ci
Bug: https://github.com/dart-lang/sdk/issues/51125
Change-Id: I9a13d57455e10865d9c5f7c12009d869a4ef0488
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279753
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/tests/vm/dart/appjit_cha_deopt_test_body.dart b/runtime/tests/vm/dart/appjit_cha_deopt_test_body.dart
index c59f720..f8b6727 100644
--- a/runtime/tests/vm/dart/appjit_cha_deopt_test_body.dart
+++ b/runtime/tests/vm/dart/appjit_cha_deopt_test_body.dart
@@ -5,39 +5,82 @@
// Verify that app-jit snapshot contains dependencies between classes and CHA
// optimized code.
-import 'package:expect/expect.dart';
+import "package:expect/expect.dart";
class A {
- void getMyName() => getMyNameImpl();
-
- void getMyNameImpl() => "A";
+ String getMyName() => "A";
}
class B extends A {
- void getMyNameImpl() => "B";
+ String getMyName() => "B";
}
final Function makeA = () => new A();
final Function makeB = () => new B();
-void optimizeGetMyName(dynamic obj) {
- for (var i = 0; i < 100; i++) {
- obj.getMyName();
- }
- Expect.equals("A", obj.getMyName());
+@pragma("vm:never-inline")
+dependentCode1(bool isTraining) {
+ dependentCode2(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
}
-void main(List<String> args) {
+@pragma("vm:never-inline")
+dependentCode2(bool isTraining) {
+ dependentCode3(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode3(bool isTraining) {
+ dependentCode4(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode4(bool isTraining) {
+ dependentCode5(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode5(bool isTraining) {
+ dependentCode6(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode6(bool isTraining) {
+ dependentCode7(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode7(bool isTraining) {
+ dependentCode8(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode8(bool isTraining) {
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+main(List<String> args) {
final isTraining = args.contains("--train");
- final dynamic obj = (isTraining ? makeA : makeB)();
+ for (var i = 0; i < 200; i++) {
+ dependentCode1(isTraining);
+ }
if (isTraining) {
- for (var i = 0; i < 10; i++) {
- optimizeGetMyName(obj);
- }
- Expect.equals('A', obj.getMyName());
- print('OK(Trained)');
+ print("OK(Trained)");
} else {
- Expect.equals('B', obj.getMyName());
- print('OK(Run)');
+ print("OK(Run)");
}
}
diff --git a/runtime/tests/vm/dart/appjit_field_guard_deopt_test.dart b/runtime/tests/vm/dart/appjit_field_guard_deopt_test.dart
new file mode 100644
index 0000000..538a483
--- /dev/null
+++ b/runtime/tests/vm/dart/appjit_field_guard_deopt_test.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2023, 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.
+
+// OtherResources=appjit_field_guard_deopt_test_body.dart
+// VMOptions=--optimization-counter-threshold=100 --deterministic
+
+// Verify that app-jit snapshot contains dependencies between fields and
+// field-guard optimized code.
+
+import 'dart:async';
+import 'dart:io' show Platform;
+
+import 'snapshot_test_helper.dart';
+
+Future<void> main() => runAppJitTest(
+ Platform.script.resolve('appjit_field_guard_deopt_test_body.dart'));
diff --git a/runtime/tests/vm/dart/appjit_field_guard_deopt_test_body.dart b/runtime/tests/vm/dart/appjit_field_guard_deopt_test_body.dart
new file mode 100644
index 0000000..c109481
--- /dev/null
+++ b/runtime/tests/vm/dart/appjit_field_guard_deopt_test_body.dart
@@ -0,0 +1,125 @@
+// Copyright (c) 2023, 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.
+
+// Verify that app-jit snapshot contains dependencies between fields and
+// field-guard optimized code.
+
+import "package:expect/expect.dart";
+
+class _A {
+ dynamic field;
+ _A(this.field);
+}
+
+@pragma("vm:never-inline")
+dependentCode1(_A a, bool isInt, tail) {
+ dependentCode2(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode2(_A a, bool isInt, tail) {
+ dependentCode3(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode3(_A a, bool isInt, tail) {
+ dependentCode4(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode4(_A a, bool isInt, tail) {
+ dependentCode5(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode5(_A a, bool isInt, tail) {
+ dependentCode6(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode6(_A a, bool isInt, tail) {
+ dependentCode7(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode7(_A a, bool isInt, tail) {
+ dependentCode8(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode8(_A a, bool isInt, tail) {
+ tail();
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+main(List<String> args) {
+ final isTraining = args.contains("--train");
+ if (isTraining) {
+ var a = new _A(0);
+ for (var i = 0; i < 200; i++) {
+ dependentCode1(a, true, () {});
+ }
+ Expect.equals(a.field, 200 * 8);
+ print("OK(Trained)");
+ } else {
+ var a = new _A(0);
+ var b;
+ dependentCode1(a, true, () {
+ b = new _A(0.0);
+ });
+ Expect.equals(a.field, 8);
+ for (var i = 0; i < 200; i++) {
+ dependentCode1(b, false, () {});
+ }
+ Expect.equals(b.field, 200 * 8);
+ print("OK(Run)");
+ }
+}
diff --git a/runtime/tests/vm/dart_2/appjit_cha_deopt_test_body.dart b/runtime/tests/vm/dart_2/appjit_cha_deopt_test_body.dart
index 917f652..0c54348 100644
--- a/runtime/tests/vm/dart_2/appjit_cha_deopt_test_body.dart
+++ b/runtime/tests/vm/dart_2/appjit_cha_deopt_test_body.dart
@@ -7,39 +7,82 @@
// Verify that app-jit snapshot contains dependencies between classes and CHA
// optimized code.
-import 'package:expect/expect.dart';
+import "package:expect/expect.dart";
class A {
- void getMyName() => getMyNameImpl();
-
- void getMyNameImpl() => "A";
+ String getMyName() => "A";
}
class B extends A {
- void getMyNameImpl() => "B";
+ String getMyName() => "B";
}
final Function makeA = () => new A();
final Function makeB = () => new B();
-void optimizeGetMyName(dynamic obj) {
- for (var i = 0; i < 100; i++) {
- obj.getMyName();
- }
- Expect.equals("A", obj.getMyName());
+@pragma("vm:never-inline")
+dependentCode1(bool isTraining) {
+ dependentCode2(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
}
-void main(List<String> args) {
+@pragma("vm:never-inline")
+dependentCode2(bool isTraining) {
+ dependentCode3(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode3(bool isTraining) {
+ dependentCode4(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode4(bool isTraining) {
+ dependentCode5(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode5(bool isTraining) {
+ dependentCode6(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode6(bool isTraining) {
+ dependentCode7(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode7(bool isTraining) {
+ dependentCode8(isTraining);
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+@pragma("vm:never-inline")
+dependentCode8(bool isTraining) {
+ A obj = (isTraining ? makeA : makeB)();
+ Expect.equals(isTraining ? "A" : "B", obj.getMyName());
+}
+
+main(List<String> args) {
final isTraining = args.contains("--train");
- final dynamic obj = (isTraining ? makeA : makeB)();
+ for (var i = 0; i < 200; i++) {
+ dependentCode1(isTraining);
+ }
if (isTraining) {
- for (var i = 0; i < 10; i++) {
- optimizeGetMyName(obj);
- }
- Expect.equals('A', obj.getMyName());
- print('OK(Trained)');
+ print("OK(Trained)");
} else {
- Expect.equals('B', obj.getMyName());
- print('OK(Run)');
+ print("OK(Run)");
}
}
diff --git a/runtime/tests/vm/dart_2/appjit_field_guard_deopt_test.dart b/runtime/tests/vm/dart_2/appjit_field_guard_deopt_test.dart
new file mode 100644
index 0000000..2f28d12
--- /dev/null
+++ b/runtime/tests/vm/dart_2/appjit_field_guard_deopt_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2023, 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.
+
+// @dart = 2.9
+
+// OtherResources=appjit_field_guard_deopt_test_body.dart
+// VMOptions=--optimization-counter-threshold=100 --deterministic
+
+// Verify that app-jit snapshot contains dependencies between fields and
+// field-guard optimized code.
+
+import 'dart:async';
+import 'dart:io' show Platform;
+
+import 'snapshot_test_helper.dart';
+
+Future<void> main() => runAppJitTest(
+ Platform.script.resolve('appjit_field_guard_deopt_test_body.dart'));
diff --git a/runtime/tests/vm/dart_2/appjit_field_guard_deopt_test_body.dart b/runtime/tests/vm/dart_2/appjit_field_guard_deopt_test_body.dart
new file mode 100644
index 0000000..0eab436
--- /dev/null
+++ b/runtime/tests/vm/dart_2/appjit_field_guard_deopt_test_body.dart
@@ -0,0 +1,127 @@
+// Copyright (c) 2023, 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.
+
+// @dart = 2.9
+
+// Verify that app-jit snapshot contains dependencies between fields and
+// field-guard optimized code.
+
+import "package:expect/expect.dart";
+
+class _A {
+ dynamic field;
+ _A(this.field);
+}
+
+@pragma("vm:never-inline")
+dependentCode1(_A a, bool isInt, tail) {
+ dependentCode2(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode2(_A a, bool isInt, tail) {
+ dependentCode3(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode3(_A a, bool isInt, tail) {
+ dependentCode4(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode4(_A a, bool isInt, tail) {
+ dependentCode5(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode5(_A a, bool isInt, tail) {
+ dependentCode6(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode6(_A a, bool isInt, tail) {
+ dependentCode7(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode7(_A a, bool isInt, tail) {
+ dependentCode8(a, isInt, tail);
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+@pragma("vm:never-inline")
+dependentCode8(_A a, bool isInt, tail) {
+ tail();
+ a.field++;
+ if (isInt) {
+ Expect.type<int>(a.field);
+ } else {
+ Expect.type<double>(a.field);
+ }
+}
+
+main(List<String> args) {
+ final isTraining = args.contains("--train");
+ if (isTraining) {
+ var a = new _A(0);
+ for (var i = 0; i < 200; i++) {
+ dependentCode1(a, true, () {});
+ }
+ Expect.equals(a.field, 200 * 8);
+ print("OK(Trained)");
+ } else {
+ var a = new _A(0);
+ var b;
+ dependentCode1(a, true, () {
+ b = new _A(0.0);
+ });
+ Expect.equals(a.field, 8);
+ for (var i = 0; i < 200; i++) {
+ dependentCode1(b, false, () {});
+ }
+ Expect.equals(b.field, 200 * 8);
+ print("OK(Run)");
+ }
+}
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index 0bb1ded..7ac7fd3 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -1534,7 +1534,8 @@
ASSERT(d_->kind() != Snapshot::kFullAOT);
field->untag()->guarded_list_length_ = static_cast<SmiPtr>(d.ReadRef());
if (kind == Snapshot::kFullJIT) {
- field->untag()->dependent_code_ = static_cast<ArrayPtr>(d.ReadRef());
+ field->untag()->dependent_code_ =
+ static_cast<WeakArrayPtr>(d.ReadRef());
}
field->untag()->token_pos_ = d.ReadTokenPosition();
field->untag()->end_token_pos_ = d.ReadTokenPosition();
diff --git a/runtime/vm/compiler/aot/precompiler_tracer.h b/runtime/vm/compiler/aot/precompiler_tracer.h
index 324e82c..99385cb 100644
--- a/runtime/vm/compiler/aot/precompiler_tracer.h
+++ b/runtime/vm/compiler/aot/precompiler_tracer.h
@@ -99,9 +99,9 @@
if (obj.IsFunction()) {
return Function::Cast(obj).Hash();
} else if (obj.IsClass()) {
- return String::HashRawSymbol(Class::Cast(obj).Name());
+ return Class::Cast(obj).Hash();
} else if (obj.IsField()) {
- return String::HashRawSymbol(Field::Cast(obj).name());
+ return Field::Cast(obj).Hash();
}
return obj.GetClassId();
}
diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc
index 0cba356..2315c90 100644
--- a/runtime/vm/compiler/runtime_api.cc
+++ b/runtime/vm/compiler/runtime_api.cc
@@ -113,14 +113,13 @@
return Instance::Cast(obj).CanonicalizeHash();
}
if (obj.IsCode()) {
- // Instructions don't move during compaction.
- return Code::Cast(obj).PayloadStart();
+ return Code::Cast(obj).Hash();
}
if (obj.IsFunction()) {
return Function::Cast(obj).Hash();
}
if (obj.IsField()) {
- return dart::String::HashRawSymbol(Field::Cast(obj).name());
+ return Field::Cast(obj).Hash();
}
if (obj.IsICData()) {
return ICData::Cast(obj).Hash();
diff --git a/runtime/vm/heap/weak_code.cc b/runtime/vm/heap/weak_code.cc
index 5e59703..0989d73 100644
--- a/runtime/vm/heap/weak_code.cc
+++ b/runtime/vm/heap/weak_code.cc
@@ -7,6 +7,7 @@
#include "platform/assert.h"
#include "vm/code_patcher.h"
+#include "vm/hash_table.h"
#include "vm/object.h"
#include "vm/runtime_entry.h"
#include "vm/stack_frame.h"
@@ -14,65 +15,44 @@
namespace dart {
+class CodeTraits {
+ public:
+ static const char* Name() { return "CodeTraits"; }
+ static bool ReportStats() { return false; }
+ static bool IsMatch(const Object& a, const Object& b) {
+ return a.ptr() == b.ptr();
+ }
+ static uword Hash(const Object& key) { return Code::Cast(key).Hash(); }
+};
+
+typedef UnorderedHashSet<CodeTraits, WeakArrayStorageTraits> WeakCodeSet;
+
bool WeakCodeReferences::HasCodes() const {
return !array_.IsNull() && (array_.Length() > 0);
}
void WeakCodeReferences::Register(const Code& value) {
- if (!array_.IsNull()) {
- // Try to find and reuse cleared WeakProperty to avoid allocating new one.
- WeakProperty& weak_property = WeakProperty::Handle();
- for (intptr_t i = 0; i < array_.Length(); i++) {
- weak_property ^= array_.At(i);
- if (weak_property.key() == Code::null()) {
- // Empty property found. Reuse it.
- weak_property.set_key(value);
- return;
- }
- }
- }
-
- const WeakProperty& weak_property =
- WeakProperty::Handle(WeakProperty::New(Heap::kOld));
- weak_property.set_key(value);
-
- intptr_t length = array_.IsNull() ? 0 : array_.Length();
- const Array& new_array =
- Array::Handle(Array::Grow(array_, length + 1, Heap::kOld));
- new_array.SetAt(length, weak_property);
- UpdateArrayTo(new_array);
-}
-
-bool WeakCodeReferences::IsOptimizedCode(const Array& dependent_code,
- const Code& code) {
- if (!code.is_optimized()) {
- return false;
- }
- WeakProperty& weak_property = WeakProperty::Handle();
- for (intptr_t i = 0; i < dependent_code.Length(); i++) {
- weak_property ^= dependent_code.At(i);
- if (code.ptr() == weak_property.key()) {
- return true;
- }
- }
- return false;
+ WeakCodeSet set(array_.IsNull() ? HashTables::New<WeakCodeSet>(4, Heap::kOld)
+ : array_.ptr());
+ set.Insert(value);
+ UpdateArrayTo(set.Release());
}
void WeakCodeReferences::DisableCode(bool are_mutators_stopped) {
- Thread* thread = Thread::Current();
- const Array& code_objects = Array::Handle(thread->zone(), array_.ptr());
#if defined(DART_PRECOMPILED_RUNTIME)
- ASSERT(code_objects.IsNull());
+ ASSERT(array_.IsNull());
return;
#else
// Ensure mutators see empty code_objects only after code was deoptimized.
DEBUG_ASSERT(
IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
- if (code_objects.IsNull()) {
+ if (array_.IsNull()) {
return;
}
+ WeakCodeSet set(array_.ptr());
+
auto isolate_group = IsolateGroup::Current();
auto disable_code_fun = [&]() {
Code& code = Code::Handle();
@@ -84,7 +64,8 @@
StackFrame* frame = iterator.NextFrame();
while (frame != nullptr) {
code = frame->LookupDartCode();
- if (IsOptimizedCode(code_objects, code)) {
+
+ if (set.ContainsKey(code)) {
ReportDeoptimization(code);
DeoptimizeAt(mutator_thread, code, frame);
}
@@ -94,12 +75,11 @@
/*at_safepoint=*/true);
// Switch functions that use dependent code to unoptimized code.
- WeakProperty& weak_property = WeakProperty::Handle();
Object& owner = Object::Handle();
Function& function = Function::Handle();
- for (intptr_t i = 0; i < code_objects.Length(); i++) {
- weak_property ^= code_objects.At(i);
- code ^= weak_property.key();
+ WeakCodeSet::Iterator it(&set);
+ while (it.MoveNext()) {
+ code ^= set.GetKey(it.Current());
if (code.IsNull()) {
// Code was garbage collected already.
continue;
@@ -138,7 +118,7 @@
}
}
- UpdateArrayTo(Object::null_array());
+ UpdateArrayTo(WeakArray::Handle());
};
// Deoptimize stacks and disable code (with mutators stopped if they are not
@@ -149,6 +129,8 @@
isolate_group->RunWithStoppedMutators(disable_code_fun);
}
+ set.Release();
+
#endif // defined(DART_PRECOMPILED_RUNTIME)
}
diff --git a/runtime/vm/heap/weak_code.h b/runtime/vm/heap/weak_code.h
index e87ac5c..c042d84 100644
--- a/runtime/vm/heap/weak_code.h
+++ b/runtime/vm/heap/weak_code.h
@@ -10,30 +10,28 @@
namespace dart {
-class Array;
+class WeakArray;
class Code;
// Helper class to handle an array of code weak properties. Implements
// registration and disabling of stored code objects.
class WeakCodeReferences : public ValueObject {
public:
- explicit WeakCodeReferences(const Array& value) : array_(value) {}
+ explicit WeakCodeReferences(const WeakArray& value) : array_(value) {}
virtual ~WeakCodeReferences() {}
void Register(const Code& value);
- virtual void UpdateArrayTo(const Array& array) = 0;
+ virtual void UpdateArrayTo(const WeakArray& array) = 0;
virtual void ReportDeoptimization(const Code& code) = 0;
virtual void ReportSwitchingCode(const Code& code) = 0;
- static bool IsOptimizedCode(const Array& dependent_code, const Code& code);
-
void DisableCode(bool are_mutators_stopped);
bool HasCodes() const;
private:
- const Array& array_; // Array of Code objects.
+ const WeakArray& array_; // Array of Code objects.
DISALLOW_COPY_AND_ASSIGN(WeakCodeReferences);
};
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 4e9cceb..5e9ca6a 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -4273,9 +4273,10 @@
class CHACodeArray : public WeakCodeReferences {
public:
explicit CHACodeArray(const Class& cls)
- : WeakCodeReferences(Array::Handle(cls.dependent_code())), cls_(cls) {}
+ : WeakCodeReferences(WeakArray::Handle(cls.dependent_code())),
+ cls_(cls) {}
- virtual void UpdateArrayTo(const Array& value) {
+ virtual void UpdateArrayTo(const WeakArray& value) {
// TODO(fschneider): Fails for classes in the VM isolate.
cls_.set_dependent_code(value);
}
@@ -4333,13 +4334,13 @@
DisableCHAOptimizedCode(Class::Handle());
}
-ArrayPtr Class::dependent_code() const {
+WeakArrayPtr Class::dependent_code() const {
DEBUG_ASSERT(
IsolateGroup::Current()->program_lock()->IsCurrentThreadReader());
return untag()->dependent_code();
}
-void Class::set_dependent_code(const Array& array) const {
+void Class::set_dependent_code(const WeakArray& array) const {
DEBUG_ASSERT(
IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
untag()->set_dependent_code(array.ptr());
@@ -5377,6 +5378,10 @@
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
+uint32_t Class::Hash() const {
+ return String::HashRawSymbol(Name());
+}
+
int32_t Class::SourceFingerprint() const {
#if !defined(DART_PRECOMPILED_RUNTIME)
return kernel::KernelSourceFingerprintHelper::CalculateClassFingerprint(
@@ -11300,6 +11305,10 @@
return PatchClass::Cast(obj).script();
}
+uint32_t Field::Hash() const {
+ return String::HashRawSymbol(name());
+}
+
ExternalTypedDataPtr Field::KernelData() const {
const Object& obj = Object::Handle(this->untag()->owner());
// During background JIT compilation field objects are copied
@@ -11623,13 +11632,13 @@
return AccessorClosure(true);
}
-ArrayPtr Field::dependent_code() const {
+WeakArrayPtr Field::dependent_code() const {
DEBUG_ASSERT(
IsolateGroup::Current()->program_lock()->IsCurrentThreadReader());
return untag()->dependent_code();
}
-void Field::set_dependent_code(const Array& array) const {
+void Field::set_dependent_code(const WeakArray& array) const {
ASSERT(IsOriginal());
DEBUG_ASSERT(
IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
@@ -11639,10 +11648,10 @@
class FieldDependentArray : public WeakCodeReferences {
public:
explicit FieldDependentArray(const Field& field)
- : WeakCodeReferences(Array::Handle(field.dependent_code())),
+ : WeakCodeReferences(WeakArray::Handle(field.dependent_code())),
field_(field) {}
- virtual void UpdateArrayTo(const Array& value) {
+ virtual void UpdateArrayTo(const WeakArray& value) {
field_.set_dependent_code(value);
}
@@ -18185,6 +18194,24 @@
kScrubbedName, NameDisambiguation::kYes)));
}
+uint32_t Code::Hash() const {
+ // PayloadStart() is a tempting hash as Instructions are not moved by the
+ // compactor, but Instructions are effectively moved between the process
+ // creating an AppJIT/AOT snapshot and the process loading the snapshot.
+ const Object& obj =
+ Object::Handle(WeakSerializationReference::UnwrapIfTarget(owner()));
+ if (obj.IsClass()) {
+ return Class::Cast(obj).Hash();
+ } else if (obj.IsAbstractType()) {
+ return AbstractType::Cast(obj).Hash();
+ } else if (obj.IsFunction()) {
+ return Function::Cast(obj).Hash();
+ } else {
+ // E.g., VM stub.
+ return 42;
+ }
+}
+
const char* Code::Name() const {
Zone* zone = Thread::Current()->zone();
if (IsStubCode()) {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 471b395..83cd2a0 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1203,6 +1203,7 @@
void set_end_token_pos(TokenPosition value) const;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
+ uint32_t Hash() const;
int32_t SourceFingerprint() const;
// Return the Type with type parameters declared by this class filled in with
@@ -1799,8 +1800,8 @@
// Return the list of code objects that were compiled using CHA of this class.
// These code objects will be invalidated if new subclasses of this class
// are finalized.
- ArrayPtr dependent_code() const;
- void set_dependent_code(const Array& array) const;
+ WeakArrayPtr dependent_code() const;
+ void set_dependent_code(const WeakArray& array) const;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
bool TraceAllocation(IsolateGroup* isolate_group) const;
@@ -4236,6 +4237,8 @@
ScriptPtr Script() const;
ObjectPtr RawOwner() const;
+ uint32_t Hash() const;
+
AbstractTypePtr type() const { return untag()->type(); }
// Used by class finalizer, otherwise initialized in constructor.
void SetFieldType(const AbstractType& value) const;
@@ -4464,8 +4467,8 @@
// assumptions about guarded class id and nullability of this field.
// These code objects must be deoptimized when field's properties change.
// Code objects are held weakly via an indirection through WeakProperty.
- ArrayPtr dependent_code() const;
- void set_dependent_code(const Array& array) const;
+ WeakArrayPtr dependent_code() const;
+ void set_dependent_code(const WeakArray& array) const;
// Add the given code object to the list of dependent ones.
void RegisterDependentCode(const Code& code) const;
@@ -6907,6 +6910,7 @@
UntaggedPcDescriptors::Kind kind) const;
intptr_t GetDeoptIdForOsr(uword pc) const;
+ uint32_t Hash() const;
const char* Name() const;
const char* QualifiedName(const NameFormattingParams& params) const;
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 267b369..46b6ec0 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1009,7 +1009,7 @@
// Stub code for allocation of instances.
COMPRESSED_POINTER_FIELD(CodePtr, allocation_stub)
// CHA optimized codes.
- COMPRESSED_POINTER_FIELD(ArrayPtr, dependent_code)
+ COMPRESSED_POINTER_FIELD(WeakArrayPtr, dependent_code)
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#if defined(DART_PRECOMPILED_RUNTIME)
@@ -1447,7 +1447,7 @@
// - for static fields: index into field_table.
COMPRESSED_POINTER_FIELD(SmiPtr, host_offset_or_field_id)
COMPRESSED_POINTER_FIELD(SmiPtr, guarded_list_length)
- COMPRESSED_POINTER_FIELD(ArrayPtr, dependent_code)
+ COMPRESSED_POINTER_FIELD(WeakArrayPtr, dependent_code)
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) {
switch (kind) {
case Snapshot::kFull: