Version 1.3.0-dev.5.2

svn merge -c 34213 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@34229 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart b/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
index ba34bb2..fdedd2a 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
@@ -816,6 +816,8 @@
   void setUseGvn() { _useGvn = true; }
   void clearUseGvn() { _useGvn = false; }
 
+  bool get isMovable => useGvn();
+
   /**
    * A pure instruction is an instruction that does not have any side
    * effect, nor any dependency. They can be moved anywhere in the
@@ -2464,16 +2466,27 @@
   bool dataEquals(HTypeConversion other) {
     return kind == other.kind
         && typeExpression == other.typeExpression
-        && checkedType == other.checkedType;
+        && checkedType == other.checkedType
+        && receiverTypeCheckSelector == other.receiverTypeCheckSelector;
   }
 }
 
 /// The [HTypeKnown] instruction marks a value with a refined type.
 class HTypeKnown extends HCheck {
   TypeMask knownType;
-  HTypeKnown(TypeMask knownType, HInstruction input)
+  bool _isMovable;
+
+  HTypeKnown.pinned(TypeMask knownType, HInstruction input)
       : this.knownType = knownType,
+        this._isMovable = false,
         super(<HInstruction>[input], knownType);
+
+  HTypeKnown.witnessed(TypeMask knownType, HInstruction input,
+                       HInstruction witness)
+      : this.knownType = knownType,
+        this._isMovable = true,
+        super(<HInstruction>[input, witness], knownType);
+
   toString() => 'TypeKnown $knownType';
   accept(HVisitor visitor) => visitor.visitTypeKnown(this);
 
@@ -2484,6 +2497,7 @@
   int typeCode() => HInstruction.TYPE_KNOWN_TYPECODE;
   bool typeEquals(HInstruction other) => other is HTypeKnown;
   bool isCodeMotionInvariant() => true;
+  bool get isMovable => _isMovable && useGvn();
 
   bool dataEquals(HTypeKnown other) {
     return knownType == other.knownType
@@ -2496,6 +2510,9 @@
       : super(<HInstruction>[input], type) {
     sourceElement = input.sourceElement;
   }
+
+  bool get isMovable => false;
+
   accept(HVisitor visitor) => visitor.visitRangeConversion(this);
 }
 
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
index b575772..3ea9d98 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
@@ -1243,7 +1243,7 @@
             && loopHeader.successors[0] == block);
     while (instruction != null) {
       HInstruction next = instruction.next;
-      if (instruction.useGvn()
+      if (instruction.useGvn() && instruction.isMovable
           && (!instruction.canThrow() || firstInstructionInLoop)
           && !instruction.sideEffects.dependsOn(dependsFlags)) {
         bool loopInvariantInputs = true;
@@ -1465,11 +1465,11 @@
 
       HInstruction current = instruction;
       instruction = instruction.next;
-
-      // TODO(ngeoffray): this check is needed because we currently do
-      // not have flags to express 'Gvn'able', but not movable.
-      if (current is HCheck) continue;
-      if (!current.useGvn()) continue;
+      if (!current.useGvn() || !current.isMovable) continue;
+      // TODO(sra): We could move throwing instructions provided we keep the
+      // exceptions in the same order.  This requires they are in the same order
+      // in all successors, which is not tracked by the ValueSet.
+      if (current.canThrow()) continue;
       if (current.sideEffects.dependsOn(dependsFlags)) continue;
 
       bool canBeMoved = true;
@@ -1504,14 +1504,16 @@
   }
 
   // Update users of [input] that are dominated by [:dominator.first:]
-  // to use [newInput] instead.
-  void changeUsesDominatedBy(HBasicBlock dominator,
-                             HInstruction input,
-                             TypeMask convertedType) {
+  // to use [TypeKnown] of [input] instead. As the type information depends
+  // on the control flow, we mark the inserted [HTypeKnown] nodes as
+  // non-movable.
+  void insertTypePropagationForDominatedUsers(HBasicBlock dominator,
+                                              HInstruction input,
+                                              TypeMask convertedType) {
     Setlet<HInstruction> dominatedUsers = input.dominatedUsers(dominator.first);
     if (dominatedUsers.isEmpty) return;
 
-    HTypeKnown newInput = new HTypeKnown(convertedType, input);
+    HTypeKnown newInput = new HTypeKnown.pinned(convertedType, input);
     dominator.addBefore(dominator.first, newInput);
     dominatedUsers.forEach((HInstruction user) {
       user.changeUse(input, newInput);
@@ -1538,12 +1540,14 @@
     HInstruction input = instruction.expression;
 
     for (HIf ifUser in ifUsers) {
-      changeUsesDominatedBy(ifUser.thenBlock, input, convertedType);
+      insertTypePropagationForDominatedUsers(ifUser.thenBlock, input,
+                                             convertedType);
       // TODO(ngeoffray): Also change uses for the else block on a type
       // that knows it is not of a specific type.
     }
     for (HIf ifUser in notIfUsers) {
-      changeUsesDominatedBy(ifUser.elseBlock, input, convertedType);
+      insertTypePropagationForDominatedUsers(ifUser.elseBlock, input,
+                                             convertedType);
       // TODO(ngeoffray): Also change uses for the then block on a type
       // that knows it is not of a specific type.
     }
@@ -1575,11 +1579,13 @@
     TypeMask nonNullType = input.instructionType.nonNullable();
 
     for (HIf ifUser in ifUsers) {
-      changeUsesDominatedBy(ifUser.elseBlock, input, nonNullType);
+      insertTypePropagationForDominatedUsers(ifUser.elseBlock, input,
+                                             nonNullType);
       // Uses in thenBlock are `null`, but probably not common.
     }
     for (HIf ifUser in notIfUsers) {
-      changeUsesDominatedBy(ifUser.thenBlock, input, nonNullType);
+      insertTypePropagationForDominatedUsers(ifUser.thenBlock, input,
+                                             nonNullType);
       // Uses in elseBlock are `null`, but probably not common.
     }
   }
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart b/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart
index 705f219..9c871fc 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart
@@ -342,7 +342,8 @@
         // Insert a refinement node after the call and update all
         // users dominated by the call to use that node instead of
         // [receiver].
-        HTypeKnown converted = new HTypeKnown(newType, receiver);
+        HTypeKnown converted =
+            new HTypeKnown.witnessed(newType, receiver, instruction);
         instruction.block.addBefore(instruction.next, converted);
         receiver.replaceAllUsersDominatedBy(converted.next, converted);
         addDependentInstructionsToWorkList(converted);
diff --git a/tests/compiler/dart2js_extra/17645_test.dart b/tests/compiler/dart2js_extra/17645_test.dart
new file mode 100644
index 0000000..67d8de8
--- /dev/null
+++ b/tests/compiler/dart2js_extra/17645_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2014, 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.
+
+import "package:expect/expect.dart";
+
+//  Regression test for issue 17645.
+get never => new DateTime.now().millisecondsSinceEpoch == 0;
+
+class A {
+  var foo;
+  A(this.foo);
+}
+
+var log = [];
+
+test1(a, xs) {  // Called with a = [null|exact=A]
+  log.clear();
+  for (var x in xs) {
+    if (a != null) {
+      log.add('${a.foo}.$x');  // a.foo must not be hoisted
+    }
+  }
+  return '$log';
+}
+
+test2(a, xs) {  // Called with a = [exact=A]
+  log.clear();
+  for (var x in xs) {
+    if (a != null) {
+      log.add('${a.foo}.$x');  // a.foo may be hoisted
+    }
+  }
+  return '$log';
+}
+
+test3(a, xs) {  // Called with a = [null|exact=A]
+  log.clear();
+  for (var x in xs) {
+    if (a is A) {
+      log.add('${a.foo}.$x');  // a.foo must not be hoisted
+    }
+  }
+  return '$log';
+}
+
+test4(a, xs) {  // Called with a = [exact=A]
+  log.clear();
+  for (var x in xs) {
+    if (a is A) {
+      log.add('${a.foo}.$x');  // a.foo may be hoisted
+    }
+  }
+  return '$log';
+}
+
+
+main() {
+  var a1 = new A('a1');
+  var a2 = new A('a2');
+
+  Expect.equals('[a1.11]', test1(a1, [11]));
+  Expect.equals('[]', test1(null, [11]));
+
+  Expect.equals('[a1.22]', test2(a1, [22]));
+  Expect.equals('[a2.22]', test2(a2, [22]));
+
+
+  Expect.equals('[a1.33]', test3(a1, [33]));
+  Expect.equals('[]', test3(null, [2]));
+
+  Expect.equals('[a1.44]', test4(a1, [44]));
+  Expect.equals('[a2.44]', test4(a2, [44]));
+}
diff --git a/tools/VERSION b/tools/VERSION
index d607301..b4a3d44 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 3
 PATCH 0
 PRERELEASE 5
-PRERELEASE_PATCH 1
+PRERELEASE_PATCH 2