Version 1.10.0-dev.1.8
svn merge -c 45141 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 45275 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@45282 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/observatory/tests/service/service.status b/runtime/observatory/tests/service/service.status
index d3c936e..ff8de84 100644
--- a/runtime/observatory/tests/service/service.status
+++ b/runtime/observatory/tests/service/service.status
@@ -9,6 +9,10 @@
[ $system == windows ]
code_test: Skip
+# Issue 23201
+[ $system == windows ]
+debugging_test: Skip
+
# Tests that depend on token positions (which differ in release mode).
[ $mode == release ]
debugging_test: Skip
diff --git a/runtime/vm/constant_propagator.cc b/runtime/vm/constant_propagator.cc
index 890f540..8cf0295 100644
--- a/runtime/vm/constant_propagator.cc
+++ b/runtime/vm/constant_propagator.cc
@@ -313,7 +313,16 @@
void ConstantPropagator::VisitRedefinition(RedefinitionInstr* instr) {
- SetValue(instr, instr->value()->definition()->constant_value());
+ // Ensure that we never remove redefinition of a constant unless we are also
+ // are guaranteed to fold away code paths that correspond to non-matching
+ // class ids. Otherwise LICM might potentially hoist incorrect code.
+ const Object& value = instr->value()->definition()->constant_value();
+ if (IsConstant(value) &&
+ CheckClassInstr::IsImmutableClassId(value.GetClassId())) {
+ SetValue(instr, value);
+ } else {
+ SetValue(instr, non_constant_);
+ }
}
@@ -783,10 +792,14 @@
SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid)));
return;
}
+
const Object& object = instr->object()->definition()->constant_value();
if (IsConstant(object)) {
- SetValue(instr, Smi::ZoneHandle(Z, Smi::New(object.GetClassId())));
- return;
+ cid = object.GetClassId();
+ if (CheckClassInstr::IsImmutableClassId(cid)) {
+ SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid)));
+ return;
+ }
}
SetValue(instr, non_constant_);
}
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index c439b41..7233ef2 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -8829,6 +8829,119 @@
}
+TEST_CASE(ExternalStringPolymorphicDeoptimize) {
+ const char* kScriptChars =
+ "const strA = 'AAAA';\n"
+ "class A {\n"
+ " static change_str(String s) native 'A_change_str';\n"
+ "}\n"
+ "compare(a, b, [i = 0]) {\n"
+ " return a.codeUnitAt(i) == b.codeUnitAt(i);\n"
+ "}\n"
+ "compareA(b, [i = 0]) {\n"
+ " return compare(strA, b, i);\n"
+ "}\n"
+ "main() {\n"
+ " var externalA = 'AA' + 'AA';\n"
+ " A.change_str(externalA);\n"
+ " compare('AA' + 'AA', strA);\n"
+ " compare(externalA, strA);\n"
+ " for (var i = 0; i < 10000; i++) compareA(strA);\n"
+ " A.change_str(strA);\n"
+ " return compareA('AA' + 'AA');\n"
+ "}\n";
+ Dart_Handle lib =
+ TestCase::LoadTestScript(kScriptChars,
+ &ExternalStringDeoptimize_native_lookup);
+ Dart_Handle result = Dart_Invoke(lib,
+ NewString("main"),
+ 0,
+ NULL);
+ EXPECT_VALID(result);
+ bool value = false;
+ result = Dart_BooleanValue(result, &value);
+ EXPECT_VALID(result);
+ EXPECT(value);
+}
+
+
+TEST_CASE(ExternalStringGuardFieldDeoptimize) {
+ const char* kScriptChars =
+ "const strA = 'AAAA';\n"
+ "class A {\n"
+ " static change_str(String s) native 'A_change_str';\n"
+ "}\n"
+ "class G { var f = 'A'; }\n"
+ "final guard = new G();\n"
+ "var shouldExternalize = false;\n"
+ "ext() { if (shouldExternalize) A.change_str(strA); }\n"
+ "compare(a, b, [i = 0]) {\n"
+ " guard.f = a;\n"
+ " ext();"
+ " return a.codeUnitAt(i) == b.codeUnitAt(i);\n"
+ "}\n"
+ "compareA(b, [i = 0]) {\n"
+ " return compare(strA, b, i);\n"
+ "}\n"
+ "main() {\n"
+ " var externalA = 'AA' + 'AA';\n"
+ " A.change_str(externalA);\n"
+ " compare('AA' + 'AA', strA);\n"
+ " for (var i = 0; i < 10000; i++) compareA(strA);\n"
+ " shouldExternalize = true;\n"
+ " return compareA('AA' + 'AA');\n"
+ "}\n";
+ Dart_Handle lib =
+ TestCase::LoadTestScript(kScriptChars,
+ &ExternalStringDeoptimize_native_lookup);
+ Dart_Handle result = Dart_Invoke(lib,
+ NewString("main"),
+ 0,
+ NULL);
+ EXPECT_VALID(result);
+ bool value = false;
+ result = Dart_BooleanValue(result, &value);
+ EXPECT_VALID(result);
+ EXPECT(value);
+}
+
+
+TEST_CASE(ExternalStringStaticFieldDeoptimize) {
+ const char* kScriptChars =
+ "const strA = 'AAAA';\n"
+ "class A {\n"
+ " static change_str(String s) native 'A_change_str';\n"
+ "}\n"
+ "class G { static final f = strA; }\n"
+ "compare(a, b, [i = 0]) {\n"
+ " return a.codeUnitAt(i) == b.codeUnitAt(i);\n"
+ "}\n"
+ "compareA(b, [i = 0]) {\n"
+ " return compare(G.f, b, i);\n"
+ "}\n"
+ "main() {\n"
+ " var externalA = 'AA' + 'AA';\n"
+ " A.change_str(externalA);\n"
+ " compare('AA' + 'AA', strA);\n"
+ " for (var i = 0; i < 10000; i++) compareA(strA);\n"
+ " A.change_str(G.f);"
+ " return compareA('AA' + 'AA');\n"
+ "}\n";
+ Dart_Handle lib =
+ TestCase::LoadTestScript(kScriptChars,
+ &ExternalStringDeoptimize_native_lookup);
+ Dart_Handle result = Dart_Invoke(lib,
+ NewString("main"),
+ 0,
+ NULL);
+ EXPECT_VALID(result);
+ bool value = false;
+ result = Dart_BooleanValue(result, &value);
+ EXPECT_VALID(result);
+ EXPECT(value);
+}
+
+
TEST_CASE(ExternalStringTrimDoubleParse) {
const char* kScriptChars =
"String str = 'A';\n"
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index 2b7001f..0d646b8 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -149,7 +149,7 @@
}
}
- HandleBranchOnNull(block);
+ HandleBranchOnStrictCompare(block);
for (intptr_t i = 0; i < block->dominated_blocks().length(); ++i) {
PropagateRecursive(block->dominated_blocks()[i]);
@@ -159,7 +159,8 @@
}
-void FlowGraphTypePropagator::HandleBranchOnNull(BlockEntryInstr* block) {
+void FlowGraphTypePropagator::HandleBranchOnStrictCompare(
+ BlockEntryInstr* block) {
BranchInstr* branch = block->last_instruction()->AsBranch();
if (branch == NULL) {
return;
@@ -318,7 +319,9 @@
void FlowGraphTypePropagator::VisitGuardFieldClass(
GuardFieldClassInstr* guard) {
const intptr_t cid = guard->field().guarded_cid();
- if ((cid == kIllegalCid) || (cid == kDynamicCid)) {
+ if ((cid == kIllegalCid) ||
+ (cid == kDynamicCid) ||
+ !CheckClassInstr::IsImmutableClassId(cid)) {
return;
}
@@ -794,13 +797,17 @@
return CompileType::Null();
}
+ intptr_t cid = value().GetClassId();
+ if (!CheckClassInstr::IsImmutableClassId(cid)) {
+ cid = kDynamicCid;
+ }
+
if (value().IsInstance()) {
- return CompileType::Create(
- value().GetClassId(),
+ return CompileType::Create(cid,
AbstractType::ZoneHandle(Instance::Cast(value()).GetType()));
} else {
// Type info for non-instance objects.
- return CompileType::FromCid(value().GetClassId());
+ return CompileType::FromCid(cid);
}
}
@@ -974,6 +981,9 @@
cid = obj.GetClassId();
}
}
+ if (!CheckClassInstr::IsImmutableClassId(cid)) {
+ cid = kDynamicCid;
+ }
return CompileType(is_nullable, cid, abstract_type);
}
diff --git a/runtime/vm/flow_graph_type_propagator.h b/runtime/vm/flow_graph_type_propagator.h
index 2e09ef3..4b1baf4 100644
--- a/runtime/vm/flow_graph_type_propagator.h
+++ b/runtime/vm/flow_graph_type_propagator.h
@@ -20,7 +20,7 @@
void Propagate();
void PropagateRecursive(BlockEntryInstr* block);
- void HandleBranchOnNull(BlockEntryInstr* block);
+ void HandleBranchOnStrictCompare(BlockEntryInstr* block);
void RollbackTo(intptr_t rollback_point);
diff --git a/tests/language/vm/licm_constant_redefinition_vm_test.dart b/tests/language/vm/licm_constant_redefinition_vm_test.dart
new file mode 100644
index 0000000..24f481e
--- /dev/null
+++ b/tests/language/vm/licm_constant_redefinition_vm_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--optimization_counter_threshold=100 --no-use-osr
+
+import "package:expect/expect.dart";
+
+class X {
+ final nested = [];
+
+ get length => nested.length;
+}
+
+loop(val) {
+ var sum = 0;
+ for (var i = 0; i < 10; i++) {
+ sum += val.length;
+ }
+ return sum;
+}
+
+// LoadField(LoadField(",", nested), length) should not be hoisted.
+// Otherwise it would crash.
+testRedef() => loop(",");
+
+main() {
+ // Provide polymorphic type feedback.
+ loop("");
+ loop(new X());
+
+ // Optimize loop with a constant argument.
+ for (var i = 0; i < 100; i++) {
+ testRedef();
+ }
+}
diff --git a/tools/VERSION b/tools/VERSION
index 518e69f..031f2b2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 10
PATCH 0
PRERELEASE 1
-PRERELEASE_PATCH 7
+PRERELEASE_PATCH 8