git cherry-pick 24158b773d29d36305ce6e8dbc1a1ce24a54e58e
git cherry-pick 68bfaf3ac349e78cf5a7ea78d48e0b51bae2e605
git cherry-pick 23ba527735b386371395cd06c4cf39381072fc89
git cherry-pick 72960de5f9a8bc36c7a6265d56c6643d93c512b5
Change-Id: Iaaafb084c940edbcc4c0383363f4365e5d904b64
diff --git a/runtime/tests/vm/dart/appjit_cha_deopt_test.dart b/runtime/tests/vm/dart/appjit_cha_deopt_test.dart
index 9c6f60f..fabc41b 100644
--- a/runtime/tests/vm/dart/appjit_cha_deopt_test.dart
+++ b/runtime/tests/vm/dart/appjit_cha_deopt_test.dart
@@ -8,34 +8,7 @@
// optimized code.
import 'dart:async';
-import 'dart:io';
-
-import 'package:expect/expect.dart';
-import 'package:path/path.dart' as p;
import 'snapshot_test_helper.dart';
-const snapshotName = 'app.jit';
-
-Future<void> main() async {
- final Directory temp = Directory.systemTemp.createTempSync();
- final snapshotPath = p.join(temp.path, 'app.jit');
- final testPath = Platform.script
- .toFilePath()
- .replaceAll(new RegExp(r'_test.dart$'), '_test_body.dart');
-
- await temp.create();
- try {
- final trainingResult = await runDartBinary('TRAINING RUN', [
- '--snapshot=$snapshotPath',
- '--snapshot-kind=app-jit',
- testPath,
- '--train'
- ]);
- expectOutput("OK(Trained)", trainingResult);
- final runResult = await runDartBinary('RUN FROM SNAPSHOT', [snapshotPath]);
- expectOutput("OK(Run)", runResult);
- } finally {
- await temp.delete(recursive: true);
- }
-}
+Future<void> main() => runAppJitTest();
diff --git a/runtime/tests/vm/dart/appjit_load_static_licm_test.dart b/runtime/tests/vm/dart/appjit_load_static_licm_test.dart
new file mode 100644
index 0000000..9f51b70
--- /dev/null
+++ b/runtime/tests/vm/dart/appjit_load_static_licm_test.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, 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 classes and CHA
+// optimized code.
+
+import 'dart:async';
+
+import 'snapshot_test_helper.dart';
+
+Future<void> main() => runAppJitTest();
diff --git a/runtime/tests/vm/dart/appjit_load_static_licm_test_body.dart b/runtime/tests/vm/dart/appjit_load_static_licm_test_body.dart
new file mode 100644
index 0000000..4006853
--- /dev/null
+++ b/runtime/tests/vm/dart/appjit_load_static_licm_test_body.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2018, 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 LoadStaticField IL instruction can't be hoisted above
+// InitStaticField instruction.
+
+import 'package:expect/expect.dart';
+
+// Classes used to trigger deoptimization when running from app-jit snapshot.
+class A {
+ void foo() {}
+}
+
+class B {
+ void foo() {}
+}
+
+// Static final field with a non-trivial initializer.
+// This field will be reset before snapshot is written.
+final field = (() => 'value')();
+
+dynamic bar(dynamic o, {bool loadField: false}) {
+ o.foo();
+ // Create loop to trigger loop invariant code motion. We are testing that
+ // field load can't be hoisted above field initialization.
+ for (var i = 0; i < 2; i++) {
+ final v = loadField ? field : null;
+ if (loadField) {
+ return v;
+ }
+ }
+ return null;
+}
+
+void main(List<String> args) {
+ final isTraining = args.contains("--train");
+ dynamic o = isTraining ? new A() : new B();
+ for (var i = 0; i < 20000; i++) {
+ bar(o, loadField: false);
+ if (isTraining) {
+ // Initialize the field when training.
+ bar(o, loadField: true);
+ }
+ }
+ Expect.equals('value', bar(o, loadField: true));
+ print(isTraining ? 'OK(Trained)' : 'OK(Run)');
+}
diff --git a/runtime/tests/vm/dart/regress_33999_test.dart b/runtime/tests/vm/dart/regress_33999_test.dart
new file mode 100644
index 0000000..933d2e0
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_33999_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2018, 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.
+
+// Test checking that canonicalization rules for AssertAssignable IL
+// instructions take into account that these instructions can be
+// on unreachable code paths.
+
+// Class with two type parameters.
+class A<U, T> {
+ T field;
+}
+
+// Class with a single type parameter
+class B<T> {
+ T field;
+}
+
+var TRUE = true;
+
+void foo(bool f) {
+ dynamic x = f ? new B<int>() : new A<String, int>();
+ if (f == TRUE) {
+ // Prevent constant folding by accessing a global
+ x.field = 10;
+ } else {
+ x.field = 10;
+ }
+}
+
+void bar() {
+ // When foo() is inlined into bar() a graph where
+ // allocation of B will flow into code-path that
+ // expects A will arise. On that code-path (which
+ // is dynamically unreachable because it is guarded
+ // by CheckClass) there will be an assert assignable
+ // against A.T.
+ // Canonicalization rule should not crash when it tries
+ // to instantiate this type.
+ foo(true);
+}
+
+void main() {
+ // Execute both paths to populate ICData.
+ foo(true);
+ foo(false);
+
+ // Force optimization of bar().
+ for (var i = 0; i < 100000; i++) bar();
+}
diff --git a/runtime/tests/vm/dart/snapshot_test_helper.dart b/runtime/tests/vm/dart/snapshot_test_helper.dart
index 2de2bbf..2eced21 100644
--- a/runtime/tests/vm/dart/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart/snapshot_test_helper.dart
@@ -94,3 +94,25 @@
await temp.delete(recursive: true);
}
}
+
+Future<void> runAppJitTest() async {
+ final Directory temp = Directory.systemTemp.createTempSync();
+ final snapshotPath = p.join(temp.path, 'app.jit');
+ final testPath = Platform.script
+ .toFilePath()
+ .replaceAll(new RegExp(r'_test.dart$'), '_test_body.dart');
+
+ try {
+ final trainingResult = await runDartBinary('TRAINING RUN', [
+ '--snapshot=$snapshotPath',
+ '--snapshot-kind=app-jit',
+ testPath,
+ '--train'
+ ]);
+ expectOutput("OK(Trained)", trainingResult);
+ final runResult = await runDartBinary('RUN FROM SNAPSHOT', [snapshotPath]);
+ expectOutput("OK(Run)", runResult);
+ } finally {
+ await temp.delete(recursive: true);
+ }
+}
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 66ef97f..aba685b 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -24,7 +24,7 @@
cc/CodeImmutability: Fail, OK # Address Sanitizer turns a crash into a failure.
[ $builder_tag == optimization_counter_threshold ]
-dart/appjit_cha_deopt_test: SkipByDesign # Test needs to a particular opt-counter value
+dart/appjit*: SkipByDesign # Test needs to a particular opt-counter value
[ $compiler == app_jit ]
dart/snapshot_version_test: Fail, OK # Expects to find script snapshot relative to Dart source.
@@ -55,7 +55,6 @@
[ $compiler == dartk ]
cc/DartAPI_New: Fail # Issue #33041
-dart/appjit_cha_deopt_test: SkipSlow # Issue 33266
dart/redirection_type_shuffling_test/00: RuntimeError, Pass
dart/redirection_type_shuffling_test/none: RuntimeError
@@ -230,11 +229,6 @@
cc/ScriptSnapshot1: Fail, Crash, OK # Script snapshots not supported in Dart 2
cc/ScriptSnapshotsUpdateSubclasses: Fail, Crash, OK # Script snapshots not supported in Dart 2
-[ $compiler == dartk && ($arch == simarm || $arch == simarm64 || $arch == simdbc || $arch == simdbc64) ]
-dart/appjit_cha_deopt_test: SkipSlow # DFE too slow
-dart/appjit_determinism_test: SkipSlow # DFE too slow
-dart/script_determinism_test: SkipSlow # DFE too slow
-
# Enabling of dartk for sim{arm,arm64,dbc64} revelaed these test failures, which
# are to be triaged. Isolate tests are skipped on purpose due to the usage of
# batch mode.
@@ -245,9 +239,8 @@
[ $compiler == dartk && ($hot_reload || $hot_reload_rollback) ]
dart/data_uri_spawn_test: Skip # Timeout
-[ $compiler != dartk && $compiler != none ]
-dart/appjit_cha_deopt_test: SkipByDesign # Test needs to run from source
-dart/appjit_determinism_test: SkipByDesign # Test needs to run from source
+[ $compiler != dartk && $compiler != dartkb && $compiler != none ]
+dart/appjit*: SkipByDesign # Test needs to run from source
dart/script_determinism_test: SkipByDesign # Test needs to run from source
[ $compiler == dartkp && ($runtime == dart_precompiled || $runtime == vm) ]
@@ -269,6 +262,10 @@
dart/inline_stack_frame_test: Skip
dart/optimized_stacktrace_line_test: Skip
+[ ($arch == simarm || $arch == simarm64 || $arch == simdbc || $arch == simdbc64) && ($compiler == dartk || $compiler == dartkb) ]
+dart/appjit*: SkipSlow # DFE too slow
+dart/script_determinism_test: SkipSlow # DFE too slow
+
# Profiler is completely disabled in SIMDBC builds.
# On the simluator stack traces produced by the Profiler do not match
# up with the real Dart stack trace and hence we don't get correct
@@ -359,8 +356,7 @@
dart/redirection_type_shuffling_test: SkipByDesign # Imports dart:mirrors
[ $hot_reload || $hot_reload_rollback ]
-dart/appjit_cha_deopt_test: SkipByDesign # Cannot reload with URI pointing to app snapshot.
-dart/appjit_determinism_test: SkipByDesign # Reload affects determinisim
+dart/appjit*: SkipByDesign # Cannot reload with URI pointing to app snapshot.
dart/script_determinism_test: SkipByDesign # Cannot reload with URI pointing to script snapshot.
dart/slow_path_shared_stub_test: SkipSlow # Too slow with --slow-path-triggers-gc flag and not relevant outside precompiled.
dart/spawn_infinite_loop_test: Skip # We can shutdown an isolate before it reloads.
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 0dd7ed5..f8beb87 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -822,9 +822,13 @@
}
const Field& LoadStaticFieldInstr::StaticField() const {
- Field& field = Field::ZoneHandle();
- field ^= field_value()->BoundConstant().raw();
- return field;
+ return Field::Cast(field_value()->BoundConstant());
+}
+
+bool LoadStaticFieldInstr::IsFieldInitialized() const {
+ const Field& field = StaticField();
+ return (field.StaticValue() != Object::sentinel().raw()) &&
+ (field.StaticValue() != Object::transition_sentinel().raw());
}
ConstantInstr::ConstantInstr(const Object& value, TokenPosition token_pos)
@@ -2489,8 +2493,14 @@
if (dst_type().IsInstantiated()) {
return this;
}
+
// For uninstantiated target types: If the instantiator and function
// type arguments are constant, instantiate the target type here.
+ // Note: these constant type arguments might not necessarily correspond
+ // to the correct instantiator because AssertAssignable might
+ // be located in the unreachable part of the graph (e.g.
+ // it might be dominated by CheckClass that always fails).
+ // This means that the code below must guard against such possibility.
ConstantInstr* constant_instantiator_type_args =
instantiator_type_arguments()->definition()->AsConstant();
ConstantInstr* constant_function_type_args =
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index f6bff3f..c77946d 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -4250,6 +4250,7 @@
virtual CompileType ComputeType() const;
const Field& StaticField() const;
+ bool IsFieldInitialized() const;
Value* field_value() const { return inputs_[0]; }
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index b34c1b5..0e4eea8 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -1377,6 +1377,16 @@
BlockEntryInstr* block = flow_graph()->preorder()[loop_it.Current()];
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
Instruction* current = it.Current();
+
+ // Treat loads of static final fields specially: we can CSE them but
+ // we should not move them around unless the field is initialized.
+ // Otherwise we might move load past the initialization.
+ if (LoadStaticFieldInstr* load = current->AsLoadStaticField()) {
+ if (load->AllowsCSE() && !load->IsFieldInitialized()) {
+ continue;
+ }
+ }
+
if ((current->AllowsCSE() ||
IsLoopInvariantLoad(loop_invariant_loads, i, current)) &&
!current->MayThrow()) {
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index ea85486..2ac08b7 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -6834,10 +6834,11 @@
Heap::kOld)) {
// For more informative error reporting, use the location of the other
// function here, since the caller will use the location of this function.
+ auto space = Thread::Current()->IsMutatorThread() ? Heap::kNew : Heap::kOld;
*bound_error = LanguageError::NewFormatted(
*bound_error, // A bound error if non null.
Script::Handle(other.script()), other.token_pos(), Report::AtLocation,
- Report::kError, Heap::kNew,
+ Report::kError, space,
"signature type '%s' of function '%s' is not a subtype of signature "
"type '%s' of function '%s'\n",
String::Handle(UserVisibleSignature()).ToCString(),
@@ -18641,6 +18642,19 @@
if (instantiator_type_arguments.IsNull()) {
return Type::DynamicType();
}
+ if (instantiator_type_arguments.Length() <= index()) {
+ // InstantiateFrom can be invoked from a compilation pipeline with
+ // mismatching type arguments vector. This can only happen for
+ // a dynamically unreachable code - which compiler can't remove
+ // statically for some reason.
+ // To prevent crashes we treat it as a bound error.
+ // (see AssertAssignableInstr::Canonicalize).
+ auto space = Thread::Current()->IsMutatorThread() ? Heap::kNew : Heap::kOld;
+ *bound_error = LanguageError::New(
+ String::Handle(String::New("Mismatching type argument vector.", space)),
+ Report::kError, space);
+ return raw();
+ }
return instantiator_type_arguments.TypeAt(index());
// There is no need to canonicalize the instantiated type parameter, since all
// type arguments are canonicalized at type finalization time. It would be too