[vm] Remove redundant type-checks from core libraries.

In conjunction with previous revisions, this improves several Flutter microbenchmarks by 2-4%.

Change-Id: Ic864fd586dc57d87b80cebd6ceace0a8165fa82b
Reviewed-on: https://dart-review.googlesource.com/c/88702
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/dev_compiler/tool/input_sdk/patch/collection_patch.dart b/pkg/dev_compiler/tool/input_sdk/patch/collection_patch.dart
index ab94a36..781a0c9 100644
--- a/pkg/dev_compiler/tool/input_sdk/patch/collection_patch.dart
+++ b/pkg/dev_compiler/tool/input_sdk/patch/collection_patch.dart
@@ -572,3 +572,30 @@
         iterator);
   }
 }
+
+@patch
+abstract class _SplayTree<K, Node extends _SplayTreeNode<K>> {
+  @patch
+  Node _splayMin(Node node) {
+    Node current = node;
+    while (current.left != null) {
+      Node left = current.left;
+      current.left = left.right;
+      left.right = current;
+      current = left;
+    }
+    return current;
+  }
+
+  @patch
+  Node _splayMax(Node node) {
+    Node current = node;
+    while (current.right != null) {
+      Node right = current.right;
+      current.right = right.left;
+      right.left = current;
+      current = right;
+    }
+    return current;
+  }
+}
diff --git a/runtime/lib/array_patch.dart b/runtime/lib/array_patch.dart
index 1ec264d..7bea7c9 100644
--- a/runtime/lib/array_patch.dart
+++ b/runtime/lib/array_patch.dart
@@ -24,7 +24,7 @@
 
   @patch
   factory List.from(Iterable elements, {bool growable: true}) {
-    if (elements is EfficientLengthIterable) {
+    if (elements is EfficientLengthIterable<E>) {
       int length = elements.length;
       var list = growable ? new _GrowableList<E>(length) : new _List<E>(length);
       if (length > 0) {
@@ -36,12 +36,25 @@
       }
       return list;
     }
-    List<E> list = new _GrowableList<E>(0);
-    for (E e in elements) {
-      list.add(e);
+    // If elements is an Iterable<E>, we won't need a type-test for each
+    // element. In the "common case" that elements is an Iterable<E>, this
+    // replaces a type-test on every element with a single type-test before
+    // starting the loop.
+    if (elements is Iterable<E>) {
+      List<E> list = new _GrowableList<E>(0);
+      for (E e in elements) {
+        list.add(e);
+      }
+      if (growable) return list;
+      return makeListFixedLength(list);
+    } else {
+      List<E> list = new _GrowableList<E>(0);
+      for (E e in elements) {
+        list.add(e);
+      }
+      if (growable) return list;
+      return makeListFixedLength(list);
     }
-    if (growable) return list;
-    return makeListFixedLength(list);
   }
 
   @patch
diff --git a/runtime/lib/collection_patch.dart b/runtime/lib/collection_patch.dart
index 07d6afb5..83f12ca 100644
--- a/runtime/lib/collection_patch.dart
+++ b/runtime/lib/collection_patch.dart
@@ -908,3 +908,31 @@
   @patch
   factory LinkedHashSet.identity() => new _CompactLinkedIdentityHashSet<E>();
 }
+
+@patch
+abstract class _SplayTree<K, Node extends _SplayTreeNode<K>> {
+  // We override _splayMin and _splayMax to optimize type-checks.
+  @patch
+  Node _splayMin(Node node) {
+    Node current = node;
+    while (current.left != null) {
+      Node left = internal.unsafeCast<Node>(current.left);
+      current.left = left.right;
+      left.right = current;
+      current = left;
+    }
+    return current;
+  }
+
+  @patch
+  Node _splayMax(Node node) {
+    Node current = node;
+    while (current.right != null) {
+      Node right = internal.unsafeCast<Node>(current.right);
+      current.right = right.left;
+      right.left = current;
+      current = right;
+    }
+    return current;
+  }
+}
diff --git a/runtime/lib/compact_hash.dart b/runtime/lib/compact_hash.dart
index 20ddf7a..8123b0f 100644
--- a/runtime/lib/compact_hash.dart
+++ b/runtime/lib/compact_hash.dart
@@ -433,7 +433,8 @@
   final int _checkSum;
   E current;
 
-  _CompactIterator(table, this._data, this._len, this._offset, this._step)
+  _CompactIterator(
+      _HashBase table, this._data, this._len, this._offset, this._step)
       : _table = table,
         _checkSum = table._checkSum;
 
@@ -445,7 +446,7 @@
       _offset += _step;
     } while (_offset < _len && _HashBase._isDeleted(_data, _data[_offset]));
     if (_offset < _len) {
-      current = _data[_offset];
+      current = internal.unsafeCast<E>(_data[_offset]);
       return true;
     } else {
       current = null;
diff --git a/runtime/lib/core_patch.dart b/runtime/lib/core_patch.dart
index 1b0f849..7b1876d6 100644
--- a/runtime/lib/core_patch.dart
+++ b/runtime/lib/core_patch.dart
@@ -24,7 +24,8 @@
         is64Bit,
         makeFixedListUnmodifiable,
         makeListFixedLength,
-        patch;
+        patch,
+        unsafeCast;
 
 import "dart:async" show Completer, Future, Timer;
 
@@ -116,8 +117,8 @@
   Iterator<T> get iterator {
     // Note: _Closure._clone returns _Closure which is not related to
     // _SyncGeneratorCallback, which means we need explicit cast.
-    return new _SyncIterator<T>(
-        (_moveNextFn as _Closure)._clone() as _SyncGeneratorCallback<T>);
+    return new _SyncIterator<T>(unsafeCast<_SyncGeneratorCallback<T>>(
+        unsafeCast<_Closure>(_moveNextFn)._clone()));
   }
 }
 
diff --git a/sdk/lib/_internal/js_runtime/lib/collection_patch.dart b/sdk/lib/_internal/js_runtime/lib/collection_patch.dart
index 95e7a7a..fc7a3bf 100644
--- a/sdk/lib/_internal/js_runtime/lib/collection_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/collection_patch.dart
@@ -1673,3 +1673,30 @@
     }
   }
 }
+
+@patch
+abstract class _SplayTree<K, Node extends _SplayTreeNode<K>> {
+  @patch
+  Node _splayMin(Node node) {
+    Node current = node;
+    while (current.left != null) {
+      Node left = current.left;
+      current.left = left.right;
+      left.right = current;
+      current = left;
+    }
+    return current;
+  }
+
+  @patch
+  Node _splayMax(Node node) {
+    Node current = node;
+    while (current.right != null) {
+      Node right = current.right;
+      current.right = right.left;
+      right.left = current;
+      current = right;
+    }
+    return current;
+  }
+}
diff --git a/sdk/lib/collection/splay_tree.dart b/sdk/lib/collection/splay_tree.dart
index 5bc9a6c..9856d55 100644
--- a/sdk/lib/collection/splay_tree.dart
+++ b/sdk/lib/collection/splay_tree.dart
@@ -147,32 +147,14 @@
   // anchored at [node].
   // and that node is returned. It should replace the reference to [node]
   // in any parent tree or root pointer.
-  Node _splayMin(Node node) {
-    Node current = node;
-    while (current.left != null) {
-      Node left = current.left;
-      current.left = left.right;
-      left.right = current;
-      current = left;
-    }
-    return current;
-  }
+  external Node _splayMin(Node node);
 
   // Emulates splaying with a key that is greater than any in the subtree
   // anchored at [node].
   // After this, the largest element in the tree is the root of the subtree,
   // and that node is returned. It should replace the reference to [node]
   // in any parent tree or root pointer.
-  Node _splayMax(Node node) {
-    Node current = node;
-    while (current.right != null) {
-      Node right = current.right;
-      current.right = right.left;
-      right.left = current;
-      current = right;
-    }
-    return current;
-  }
+  external Node _splayMax(Node node);
 
   Node _remove(K key) {
     if (_root == null) return null;