Version 1.6.0-dev.9.2

svn merge -c 39194 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 39197 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 39200 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 39246 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@39247 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/vm/cha.cc b/runtime/vm/cha.cc
index 5c5ccb0..b937357 100644
--- a/runtime/vm/cha.cc
+++ b/runtime/vm/cha.cc
@@ -12,81 +12,84 @@
 
 namespace dart {
 
-bool CHA::HasSubclasses(intptr_t cid) {
-  Isolate* isolate = Isolate::Current();
-  const bool has_subclasses = HasSubclassesSafe(cid);
-  if (!has_subclasses) {
-    isolate->set_cha_used(true);
-  }
-  return has_subclasses;
-}
-
-
-bool CHA::HasSubclassesSafe(intptr_t cid) {
-  ASSERT(cid >= kInstanceCid);
-  Isolate* isolate = Isolate::Current();
-  const ClassTable& class_table = *isolate->class_table();
-  const Class& cls = Class::Handle(isolate, class_table.At(cid));
-  ASSERT(!cls.IsNull());
-  if (cls.IsObjectClass()) {
-    // Class Object has subclasses, although we do not keep track of them.
-    return true;
-  }
-  const GrowableObjectArray& cls_direct_subclasses =
-      GrowableObjectArray::Handle(isolate, cls.direct_subclasses());
-  return
-      !cls_direct_subclasses.IsNull() && (cls_direct_subclasses.Length() > 0);
-}
-
-
-// Returns true if the given array of cids contains the given cid.
-static bool ContainsCid(ZoneGrowableArray<intptr_t>* cids, intptr_t cid) {
-  for (intptr_t i = 0; i < cids->length(); i++) {
-    if ((*cids)[i] == cid) {
-      return true;
-    }
-  }
+// Return true if the class is private to our internal libraries (not extendable
+// or implementable after startup). Therefore, we don't need to register
+// optimized code for invalidation for those classes.
+// TODO(fschneider): Allow more libraries.
+static bool IsKnownPrivateClass(const Class& type_class) {
+  if (!Library::IsPrivate(String::Handle(type_class.Name()))) return false;
+  const Library& library = Library::Handle(type_class.library());
+  if (library.raw() == Library::CoreLibrary()) return true;
+  if (library.raw() == Library::CollectionLibrary()) return true;
+  if (library.raw() == Library::TypedDataLibrary()) return true;
+  if (library.raw() == Library::MathLibrary()) return true;
   return false;
 }
 
 
-// Recursively collect direct and indirect subclass ids of cls.
-static void CollectSubclassIds(ZoneGrowableArray<intptr_t>* cids,
-                               const Class& cls) {
-  const GrowableObjectArray& cls_direct_subclasses =
-      GrowableObjectArray::Handle(cls.direct_subclasses());
-  if (cls_direct_subclasses.IsNull()) {
-    return;
-  }
-  Class& direct_subclass = Class::Handle();
-  for (intptr_t i = 0; i < cls_direct_subclasses.Length(); i++) {
-    direct_subclass ^= cls_direct_subclasses.At(i);
-    intptr_t direct_subclass_id = direct_subclass.id();
-    if (!ContainsCid(cids, direct_subclass_id)) {
-      cids->Add(direct_subclass_id);
-      CollectSubclassIds(cids, direct_subclass);
+void CHA::AddToLeafClasses(const Class& cls) {
+  if (IsKnownPrivateClass(cls)) return;
+
+  for (intptr_t i = 0; i < leaf_classes_.length(); i++) {
+    if (leaf_classes_[i]->raw() == cls.raw()) {
+      return;
     }
   }
+  leaf_classes_.Add(&Class::ZoneHandle(isolate_, cls.raw()));
 }
 
 
-ZoneGrowableArray<intptr_t>* CHA::GetSubclassIdsOf(intptr_t cid) {
-  ASSERT(cid > kInstanceCid);
-  Isolate* isolate = Isolate::Current();
-  const ClassTable& class_table = *isolate->class_table();
-  const Class& cls = Class::Handle(isolate, class_table.At(cid));
+bool CHA::HasSubclasses(const Class& cls) {
   ASSERT(!cls.IsNull());
-  ZoneGrowableArray<intptr_t>* ids = new ZoneGrowableArray<intptr_t>();
-  CollectSubclassIds(ids, cls);
-  isolate->set_cha_used(true);
-  return ids;
+  ASSERT(cls.id() >= kInstanceCid);
+  // Can't track dependencies for classes on the VM heap since those are
+  // read-only.
+  // TODO(fschneider): Enable tracking of CHA dependent code for VM heap
+  // classes.
+  if (cls.InVMHeap()) return true;
+
+  if (cls.IsObjectClass()) {
+    // Class Object has subclasses, although we do not keep track of them.
+    return true;
+  }
+  const GrowableObjectArray& direct_subclasses =
+      GrowableObjectArray::Handle(isolate_, cls.direct_subclasses());
+  bool result =
+      !direct_subclasses.IsNull() && (direct_subclasses.Length() > 0);
+  if (!result) {
+    AddToLeafClasses(cls);
+  }
+  return result;
+}
+
+
+bool CHA::HasSubclasses(intptr_t cid) {
+  const ClassTable& class_table = *isolate_->class_table();
+  Class& cls = Class::Handle(isolate_, class_table.At(cid));
+  return HasSubclasses(cls);
+}
+
+
+bool CHA::IsImplemented(const Class& cls) {
+  // Signature classes have different type checking rules.
+  ASSERT(!cls.IsSignatureClass());
+  // Can't track dependencies for classes on the VM heap since those are
+  // read-only.
+  // TODO(fschneider): Enable tracking of CHA dependent code for VM heap
+  // classes.
+  if (cls.InVMHeap()) return true;
+
+  bool result = cls.is_implemented();
+  if (!result) {
+    AddToLeafClasses(cls);
+  }
+  return result;
 }
 
 
 bool CHA::HasOverride(const Class& cls, const String& function_name) {
-  Isolate* isolate = Isolate::Current();
   const GrowableObjectArray& cls_direct_subclasses =
-      GrowableObjectArray::Handle(isolate, cls.direct_subclasses());
+      GrowableObjectArray::Handle(isolate_, cls.direct_subclasses());
   // Subclasses of Object are not tracked by CHA. Safely assume that overrides
   // exist.
   if (cls.IsObjectClass()) {
@@ -94,10 +97,10 @@
   }
 
   if (cls_direct_subclasses.IsNull()) {
-    isolate->set_cha_used(true);
+    AddToLeafClasses(cls);
     return false;
   }
-  Class& direct_subclass = Class::Handle(isolate);
+  Class& direct_subclass = Class::Handle(isolate_);
   for (intptr_t i = 0; i < cls_direct_subclasses.Length(); i++) {
     direct_subclass ^= cls_direct_subclasses.At(i);
     // Unfinalized classes are treated as non-existent for CHA purposes,
@@ -111,44 +114,8 @@
       return true;
     }
   }
-  isolate->set_cha_used(true);
+  AddToLeafClasses(cls);
   return false;
 }
 
-
-ZoneGrowableArray<Function*>* CHA::GetNamedInstanceFunctionsOf(
-    const ZoneGrowableArray<intptr_t>& cids,
-    const String& function_name) {
-  Isolate* isolate = Isolate::Current();
-  ASSERT(!function_name.IsNull());
-  const ClassTable& class_table = *isolate->class_table();
-  ZoneGrowableArray<Function*>* functions = new ZoneGrowableArray<Function*>();
-  Class& cls = Class::Handle(isolate);
-  Function& cls_function = Function::Handle(isolate);
-  for (intptr_t i = 0; i < cids.length(); i++) {
-    const intptr_t cid = cids[i];
-    ASSERT(cid > kInstanceCid);
-    cls = class_table.At(cid);
-    cls_function = cls.LookupDynamicFunction(function_name);
-    if (!cls_function.IsNull()) {
-      functions->Add(&Function::ZoneHandle(isolate, cls_function.raw()));
-    }
-  }
-  isolate->set_cha_used(true);
-  return functions;
-}
-
-
-ZoneGrowableArray<Function*>* CHA::GetOverridesOf(const Function& function) {
-  ASSERT(!function.IsNull());
-  ASSERT(function.IsDynamicFunction());
-  Isolate* isolate = Isolate::Current();
-  const Class& function_owner = Class::Handle(isolate, function.Owner());
-  const String& function_name = String::Handle(isolate, function.name());
-  ZoneGrowableArray<intptr_t>* cids = new ZoneGrowableArray<intptr_t>();
-  CollectSubclassIds(cids, function_owner);
-  isolate->set_cha_used(true);
-  return GetNamedInstanceFunctionsOf(*cids, function_name);
-}
-
 }  // namespace dart
diff --git a/runtime/vm/cha.h b/runtime/vm/cha.h
index 18a0bcb..2100820 100644
--- a/runtime/vm/cha.h
+++ b/runtime/vm/cha.h
@@ -6,6 +6,7 @@
 #define VM_CHA_H_
 
 #include "vm/allocation.h"
+#include "vm/growable_array.h"
 
 namespace dart {
 
@@ -14,33 +15,47 @@
 template <typename T> class ZoneGrowableArray;
 class String;
 
-class CHA : public AllStatic {
+class CHA : public StackResource {
  public:
-  // Returns true if the class given by its cid has subclasses.
-  static bool HasSubclasses(intptr_t cid);
+  explicit CHA(Isolate* isolate)
+      : StackResource(isolate),
+        isolate_(isolate),
+        leaf_classes_(isolate, 1),
+        previous_(isolate->cha()) {
+    isolate->set_cha(this);
+  }
 
-  // Use only on known private classes that can never be subclassed by lazy
-  // class finalization. Does not affect Isolate::use_cha flag.
-  static bool HasSubclassesSafe(intptr_t cid);
+  ~CHA() {
+    ASSERT(isolate_->cha() == this);
+    isolate_->set_cha(previous_);
+  }
 
-  // Returns an array containing the cids of the direct and indirect subclasses
-  // of the class given by its cid.
-  // Must not be called for kInstanceCid.
-  static ZoneGrowableArray<intptr_t>* GetSubclassIdsOf(intptr_t cid);
+  // Returns true if the class has subclasses.
+  // Updates set of leaf classes that we register optimized code with for lazy
+  // deoptimization.
+  bool HasSubclasses(const Class& cls);
+  bool HasSubclasses(intptr_t cid);
 
-  // Returns an array containing instance functions of the given name and
-  // belonging to the classes given by their cids.
-  // Cids must not contain kInstanceCid.
-  static ZoneGrowableArray<Function*>* GetNamedInstanceFunctionsOf(
-      const ZoneGrowableArray<intptr_t>& cids,
-      const String& function_name);
-
-  // Returns an array of functions overriding the given function.
-  // Must not be called for a function of class Object.
-  static ZoneGrowableArray<Function*>* GetOverridesOf(const Function& function);
+  // Return true if the class is implemented by some other class.
+  // Updates set of leaf classes that we register optimized code with for lazy
+  // deoptimization.
+  bool IsImplemented(const Class& cls);
 
   // Returns true if any subclass of 'cls' contains the function.
-  static bool HasOverride(const Class& cls, const String& function_name);
+  // Updates set of leaf classes that we register optimized code with for lazy
+  // deoptimization.
+  bool HasOverride(const Class& cls, const String& function_name);
+
+  const GrowableArray<Class*>& leaf_classes() const {
+    return leaf_classes_;
+  }
+
+ private:
+  void AddToLeafClasses(const Class& cls);
+
+  Isolate* isolate_;
+  GrowableArray<Class*> leaf_classes_;
+  CHA* previous_;
 };
 
 }  // namespace dart
diff --git a/runtime/vm/cha_test.cc b/runtime/vm/cha_test.cc
index 1ec35c5..a12a3be 100644
--- a/runtime/vm/cha_test.cc
+++ b/runtime/vm/cha_test.cc
@@ -11,6 +11,14 @@
 
 namespace dart {
 
+static bool ContainsCid(const GrowableArray<Class*>& classes, intptr_t cid) {
+  for (intptr_t i = 0; i < classes.length(); ++i) {
+    if (classes[i]->id() == cid) return true;
+  }
+  return false;
+}
+
+
 TEST_CASE(ClassHierarchyAnalysis) {
   const char* kScriptChars =
       "class A {"
@@ -36,22 +44,18 @@
   const Class& class_a = Class::Handle(
       lib.LookupClass(String::Handle(Symbols::New("A"))));
   EXPECT(!class_a.IsNull());
-  const intptr_t class_a_id = class_a.id();
 
   const Class& class_b = Class::Handle(
       lib.LookupClass(String::Handle(Symbols::New("B"))));
   EXPECT(!class_b.IsNull());
-  const intptr_t class_b_id = class_b.id();
 
   const Class& class_c = Class::Handle(
       lib.LookupClass(String::Handle(Symbols::New("C"))));
   EXPECT(!class_c.IsNull());
-  const intptr_t class_c_id = class_c.id();
 
   const Class& class_d = Class::Handle(
       lib.LookupClass(String::Handle(Symbols::New("D"))));
   EXPECT(!class_d.IsNull());
-  const intptr_t class_d_id = class_d.id();
 
   const String& function_foo_name = String::Handle(String::New("foo"));
   const String& function_bar_name = String::Handle(String::New("bar"));
@@ -76,59 +80,26 @@
       Function::Handle(class_d.LookupDynamicFunction(function_bar_name));
   EXPECT(!class_d_bar.IsNull());
 
-  ZoneGrowableArray<intptr_t>* a_subclass_ids =
-      CHA::GetSubclassIdsOf(class_a_id);
-  EXPECT_EQ(3, a_subclass_ids->length());
-  EXPECT_EQ(class_b_id, (*a_subclass_ids)[0]);
-  EXPECT_EQ(class_c_id, (*a_subclass_ids)[1]);
-  EXPECT_EQ(class_d_id, (*a_subclass_ids)[2]);
-  ZoneGrowableArray<intptr_t>* b_subclass_ids =
-      CHA::GetSubclassIdsOf(class_b_id);
-  EXPECT_EQ(1, b_subclass_ids->length());
-  EXPECT_EQ(class_c_id, (*b_subclass_ids)[0]);
-  ZoneGrowableArray<intptr_t>* c_subclass_ids =
-      CHA::GetSubclassIdsOf(class_c_id);
-  EXPECT_EQ(0, c_subclass_ids->length());
-  ZoneGrowableArray<intptr_t>* d_subclass_ids =
-      CHA::GetSubclassIdsOf(class_d_id);
-  EXPECT_EQ(0, d_subclass_ids->length());
+  CHA cha(Isolate::Current());
 
-  ZoneGrowableArray<Function*>* foos =
-      CHA::GetNamedInstanceFunctionsOf(*a_subclass_ids, function_foo_name);
-  EXPECT_EQ(2, foos->length());
-  EXPECT_EQ(class_c_foo.raw(), (*foos)[0]->raw());
-  EXPECT_EQ(class_d_foo.raw(), (*foos)[1]->raw());
+  EXPECT(cha.HasSubclasses(kInstanceCid));
+  EXPECT(!cha.HasSubclasses(kSmiCid));
+  EXPECT(!cha.HasSubclasses(kNullCid));
 
-  ZoneGrowableArray<Function*>* class_a_foo_overrides =
-      CHA::GetOverridesOf(class_a_foo);
-  EXPECT_EQ(2, class_a_foo_overrides->length());
-  EXPECT_EQ(class_c_foo.raw(), (*class_a_foo_overrides)[0]->raw());
-  EXPECT_EQ(class_d_foo.raw(), (*class_a_foo_overrides)[1]->raw());
+  EXPECT(cha.HasSubclasses(class_a));
+  EXPECT(cha.HasSubclasses(class_b));
+  EXPECT(!cha.HasSubclasses(class_c));
+  EXPECT(!cha.HasSubclasses(class_d));
 
-  ZoneGrowableArray<Function*>* bars =
-      CHA::GetNamedInstanceFunctionsOf(*a_subclass_ids, function_bar_name);
-  EXPECT_EQ(1, bars->length());
-  EXPECT_EQ(class_d_bar.raw(), (*bars)[0]->raw());
-
-  ZoneGrowableArray<Function*>* class_a_bar_overrides =
-      CHA::GetOverridesOf(class_a_bar);
-  EXPECT_EQ(1, class_a_bar_overrides->length());
-  EXPECT_EQ(class_d_bar.raw(), (*class_a_bar_overrides)[0]->raw());
-
-  EXPECT(CHA::HasSubclasses(kInstanceCid));
-  EXPECT(!CHA::HasSubclasses(kSmiCid));
-  EXPECT(!CHA::HasSubclasses(kNullCid));
-  EXPECT(!CHA::HasSubclasses(kDynamicCid));
-  EXPECT(!CHA::HasSubclasses(kVoidCid));
-  EXPECT(CHA::HasSubclasses(class_a_id));
-  EXPECT(CHA::HasSubclasses(class_b_id));
-  EXPECT(!CHA::HasSubclasses(class_c_id));
-  EXPECT(!CHA::HasSubclasses(class_d_id));
+  EXPECT(!ContainsCid(cha.leaf_classes(), class_a.id()));
+  EXPECT(!ContainsCid(cha.leaf_classes(), class_b.id()));
+  EXPECT(ContainsCid(cha.leaf_classes(), class_c.id()));
+  EXPECT(ContainsCid(cha.leaf_classes(), class_d.id()));
 
   class Class& function_impl_class =
       Class::Handle(Type::Handle(Isolate::Current()->object_store()->
           function_impl_type()).type_class());
-  EXPECT(CHA::HasSubclasses(function_impl_class.id()));
+  EXPECT(cha.HasSubclasses(function_impl_class.id()));
 }
 
 }  // namespace dart
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 1f0c615..2bda885 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -93,6 +93,27 @@
 }
 
 
+static void CollectImmediateSuperInterfaces(
+    const Class& cls, GrowableArray<intptr_t>* cids) {
+  const Array& interfaces = Array::Handle(cls.interfaces());
+  Class& ifc = Class::Handle();
+  AbstractType& type = AbstractType::Handle();
+  for (intptr_t i = 0; i < interfaces.Length(); ++i) {
+    type ^= interfaces.At(i);
+    if (type.IsMalformed()) continue;
+    if (!type.HasResolvedTypeClass()) continue;
+    ifc ^= type.type_class();
+    for (intptr_t j = 0; j < cids->length(); ++j) {
+      if ((*cids)[j] == ifc.id()) {
+        // Already added.
+        return;
+      }
+    }
+    cids->Add(ifc.id());
+  }
+}
+
+
 // Processing ObjectStore::pending_classes_ occurs:
 // a) when bootstrap process completes (VerifyBootstrapClasses).
 // b) after the user classes are loaded (dart_api).
@@ -2287,8 +2308,6 @@
     // the class conflict with inherited methods.
     ApplyMixinMembers(cls);
   }
-  GrowableArray<intptr_t> added_subclass_to_cids;
-  CollectFinalizedSuperClasses(cls, &added_subclass_to_cids);
   // Ensure super class is finalized.
   const Class& super = Class::Handle(cls.SuperClass());
   if (!super.IsNull()) {
@@ -2317,7 +2336,10 @@
     CheckForLegalConstClass(cls);
   }
   if (FLAG_use_cha) {
-    RemoveCHAOptimizedCode(added_subclass_to_cids);
+    GrowableArray<intptr_t> cids;
+    CollectFinalizedSuperClasses(cls, &cids);
+    CollectImmediateSuperInterfaces(cls, &cids);
+    RemoveCHAOptimizedCode(cids);
   }
 }
 
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index 557d61c..83500cd 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -8,6 +8,7 @@
 
 #include "vm/ast_printer.h"
 #include "vm/block_scheduler.h"
+#include "vm/cha.h"
 #include "vm/code_generator.h"
 #include "vm/code_patcher.h"
 #include "vm/dart_entry.h"
@@ -252,7 +253,6 @@
   bool is_compiled = false;
   Isolate* isolate = Isolate::Current();
   HANDLESCOPE(isolate);
-  isolate->set_cha_used(false);
 
   // We may reattempt compilation if the function needs to be assembled using
   // far branches on ARM and MIPS. In the else branch of the setjmp call,
@@ -269,6 +269,11 @@
     LongJumpScope jump;
     if (setjmp(*jump.Set()) == 0) {
       FlowGraph* flow_graph = NULL;
+
+      // Class hierarchy analysis is registered with the isolate in the
+      // constructor and unregisters itself upon destruction.
+      CHA cha(isolate);
+
       // TimerScope needs an isolate to be properly terminated in case of a
       // LongJump.
       {
@@ -545,12 +550,6 @@
         const Code& code = Code::Handle(
             Code::FinalizeCode(function, &assembler, optimized));
         code.set_is_optimized(optimized);
-        // CHA should not be used for unoptimized code.
-        ASSERT(optimized || !isolate->cha_used());
-        if (isolate->cha_used()) {
-          Class::Handle(function.Owner()).RegisterCHACode(code);
-          isolate->set_cha_used(false);
-        }
         graph_compiler.FinalizePcDescriptors(code);
         graph_compiler.FinalizeDeoptInfo(code);
         graph_compiler.FinalizeStackmaps(code);
@@ -572,6 +571,13 @@
           }
           function.AttachCode(code);
 
+          // Register code with the classes it depends on because of CHA.
+          for (intptr_t i = 0;
+               i < isolate->cha()->leaf_classes().length();
+               ++i) {
+            isolate->cha()->leaf_classes()[i]->RegisterCHACode(code);
+          }
+
           for (intptr_t i = 0;
                i < flow_graph->guarded_fields()->length();
                i++) {
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index e9d9241..fb8a0a6 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -2275,8 +2275,8 @@
   if (function.IsDynamicFunction() &&
       callee_receiver->IsParameter() &&
       (callee_receiver->AsParameter()->index() == 0)) {
-    return CHA::HasOverride(Class::Handle(I, function.Owner()),
-                            call->function_name());
+    return isolate()->cha()->HasOverride(Class::Handle(I, function.Owner()),
+                                         call->function_name());
   }
   return true;
 }
@@ -2293,7 +2293,8 @@
       (callee_receiver->AsParameter()->index() == 0)) {
     const String& field_name =
       String::Handle(I, Field::NameFromGetter(call->function_name()));
-    return CHA::HasOverride(Class::Handle(I, function.Owner()), field_name);
+    return isolate()->cha()->HasOverride(
+        Class::Handle(I, function.Owner()), field_name);
   }
   return true;
 }
@@ -3893,7 +3894,7 @@
 
 
 // Returns true if checking against this type is a direct class id comparison.
-static bool TypeCheckAsClassEquality(const AbstractType& type) {
+bool FlowGraphOptimizer::TypeCheckAsClassEquality(const AbstractType& type) {
   ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
   // Requires CHA.
   if (!FLAG_use_cha) return false;
@@ -3902,9 +3903,9 @@
   // Signature classes have different type checking rules.
   if (type_class.IsSignatureClass()) return false;
   // Could be an interface check?
-  if (type_class.is_implemented()) return false;
-  const intptr_t type_cid = type_class.id();
-  if (CHA::HasSubclasses(type_cid)) return false;
+  if (isolate()->cha()->IsImplemented(type_class)) return false;
+  // Check if there are subclasses.
+  if (isolate()->cha()->HasSubclasses(type_class)) return false;
   const intptr_t num_type_args = type_class.NumTypeArguments();
   if (num_type_args > 0) {
     // Only raw types can be directly compared, thus disregarding type
diff --git a/runtime/vm/flow_graph_optimizer.h b/runtime/vm/flow_graph_optimizer.h
index e9a7482..a4b12c8 100644
--- a/runtime/vm/flow_graph_optimizer.h
+++ b/runtime/vm/flow_graph_optimizer.h
@@ -125,6 +125,7 @@
   bool TryInlineInt32x4Method(InstanceCallInstr* call,
                                MethodRecognizer::Kind recognized_kind);
   void ReplaceWithInstanceOf(InstanceCallInstr* instr);
+  bool TypeCheckAsClassEquality(const AbstractType& type);
   void ReplaceWithTypeCast(InstanceCallInstr* instr);
 
   bool TryReplaceInstanceCallWithInline(InstanceCallInstr* call);
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index 9f3e942..d7e6396 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -498,20 +498,6 @@
 }
 
 
-// Return true if the class is private to our internal libraries (not extendable
-// or implementable by users).
-// (TODO): Allow more libraries.
-static bool IsKnownPrivateClass(const Class& type_class) {
-  if (!Library::IsPrivate(String::Handle(type_class.Name()))) return false;
-  const Library& library = Library::Handle(type_class.library());
-  if (library.raw() == Library::CoreLibrary()) return true;
-  if (library.raw() == Library::CollectionLibrary()) return true;
-  if (library.raw() == Library::TypedDataLibrary()) return true;
-  if (library.raw() == Library::MathLibrary()) return true;
-  return false;
-}
-
-
 intptr_t CompileType::ToNullableCid() {
   if (cid_ == kIllegalCid) {
     if (type_ == NULL) {
@@ -522,11 +508,14 @@
     } else if (type_->IsVoidType()) {
       cid_ = kNullCid;
     } else if (type_->HasResolvedTypeClass()) {
-      const Class& type_class = Class::Handle(type_->type_class());
-      if (FLAG_use_cha || IsKnownPrivateClass(type_class)) {
-        // A known private class cannot be subclassed or implemented.
-        if (!type_class.is_implemented() &&
-            !CHA::HasSubclassesSafe(type_class.id())) {
+      if (FLAG_use_cha) {
+        const Class& type_class = Class::Handle(type_->type_class());
+        CHA* cha = Isolate::Current()->cha();
+        // Don't infer a cid from an abstract type for signature classes since
+        // there can be multiple compatible classes with different cids.
+        if (!type_class.IsSignatureClass() &&
+            !cha->IsImplemented(type_class) &&
+            !cha->HasSubclasses(type_class.id())) {
           cid_ = type_class.id();
         } else {
           cid_ = kDynamicCid;
@@ -743,7 +732,7 @@
     intptr_t cid = kDynamicCid;
     if (FLAG_use_cha && type.HasResolvedTypeClass()) {
       const Class& type_class = Class::Handle(type.type_class());
-      if (!CHA::HasSubclasses(type_class.id())) {
+      if (!Isolate::Current()->cha()->HasSubclasses(type_class.id())) {
         cid = type_class.id();
       }
     }
@@ -788,15 +777,18 @@
     return ZoneCompileType::Wrap(CompileType::Null());
   }
 
-  return ZoneCompileType::Wrap(CompileType::FromAbstractType(dst_type()));
+  return ZoneCompileType::Wrap(
+      CompileType::FromAbstractType(dst_type(), value_type->is_nullable()));
 }
 
 
 bool AssertAssignableInstr::RecomputeType() {
   CompileType* value_type = value()->Type();
-  return UpdateType(value_type->IsMoreSpecificThan(dst_type())
-                      ? *value_type
-                      : CompileType::FromAbstractType(dst_type()));
+  return UpdateType(
+      value_type->IsMoreSpecificThan(dst_type())
+          ? *value_type
+          : CompileType::FromAbstractType(dst_type(),
+                                          value_type->is_nullable()));
 }
 
 
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 684ad60..eec4191 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -429,7 +429,7 @@
       stack_frame_index_(-1),
       last_allocationprofile_accumulator_reset_timestamp_(0),
       last_allocationprofile_gc_timestamp_(0),
-      cha_used_(false),
+      cha_(NULL),
       object_id_ring_(NULL),
       trace_buffer_(NULL),
       profiler_data_(NULL),
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 9f5bdae..1f37635 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -26,6 +26,7 @@
 class ApiState;
 class Array;
 class Capability;
+class CHA;
 class Class;
 class Code;
 class CodeIndexTable;
@@ -143,8 +144,8 @@
     return OFFSET_OF(Isolate, class_table_);
   }
 
-  bool cha_used() const { return cha_used_; }
-  void set_cha_used(bool value) { cha_used_ = value; }
+  CHA* cha() const { return cha_; }
+  void set_cha(CHA* value) { cha_ = value; }
 
   MegamorphicCacheTable* megamorphic_cache_table() {
     return &megamorphic_cache_table_;
@@ -647,7 +648,7 @@
   int64_t last_allocationprofile_accumulator_reset_timestamp_;
   int64_t last_allocationprofile_gc_timestamp_;
 
-  bool cha_used_;
+  CHA* cha_;
 
   // Ring buffer of objects assigned an id.
   ObjectIdRing* object_id_ring_;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 82bdd71..09058ab 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -1890,62 +1890,18 @@
 }
 
 
-class FunctionName {
- public:
-  FunctionName(const String& name, String* tmp_string)
-      : name_(name), tmp_string_(tmp_string) {}
-  bool Matches(const Function& function) const {
-    if (name_.IsSymbol()) {
-      return name_.raw() == function.name();
-    } else {
-      *tmp_string_ = function.name();
-      return name_.Equals(*tmp_string_);
-    }
-  }
-  intptr_t Hash() const { return name_.Hash(); }
- private:
-  const String& name_;
-  String* tmp_string_;
-};
-
-
-// Traits for looking up Functions by name.
-class ClassFunctionsTraits {
- public:
-  // Called when growing the table.
-  static bool IsMatch(const Object& a, const Object& b) {
-    ASSERT(a.IsFunction() && b.IsFunction());
-    // Function objects are always canonical.
-    return a.raw() == b.raw();
-  }
-  static bool IsMatch(const FunctionName& name, const Object& obj) {
-    return name.Matches(Function::Cast(obj));
-  }
-  static uword Hash(const Object& key) {
-    return String::HashRawSymbol(Function::Cast(key).name());
-  }
-  static uword Hash(const FunctionName& name) {
-    return name.Hash();
-  }
-};
-typedef UnorderedHashSet<ClassFunctionsTraits> ClassFunctionsSet;
-
-
 void Class::SetFunctions(const Array& value) const {
   ASSERT(!value.IsNull());
-  StorePointer(&raw_ptr()->functions_, value.raw());
-  const intptr_t len = value.Length();
-  ClassFunctionsSet set(HashTables::New<ClassFunctionsSet>(len));
-  if (len >= kFunctionLookupHashTreshold) {
-    Function& func = Function::Handle();
-    for (intptr_t i = 0; i < len; ++i) {
-      func ^= value.At(i);
-      // Verify that all the functions in the array have this class as owner.
-      ASSERT(func.Owner() == raw());
-      set.Insert(func);
-    }
+#if defined(DEBUG)
+  // Verify that all the functions in the array have this class as owner.
+  Function& func = Function::Handle();
+  intptr_t len = value.Length();
+  for (intptr_t i = 0; i < len; i++) {
+    func ^= value.At(i);
+    ASSERT(func.Owner() == raw());
   }
-  StorePointer(&raw_ptr()->functions_hash_table_, set.Release().raw());
+#endif
+  StorePointer(&raw_ptr()->functions_, value.raw());
 }
 
 
@@ -1953,17 +1909,7 @@
   const Array& arr = Array::Handle(functions());
   const Array& new_arr = Array::Handle(Array::Grow(arr, arr.Length() + 1));
   new_arr.SetAt(arr.Length(), function);
-  StorePointer(&raw_ptr()->functions_, new_arr.raw());
-  // Add to hash table, if any.
-  const intptr_t new_len = new_arr.Length();
-  if (new_len == kFunctionLookupHashTreshold) {
-    // Transition to using hash table.
-    SetFunctions(new_arr);
-  } else if (new_len > kFunctionLookupHashTreshold) {
-    ClassFunctionsSet set(raw_ptr()->functions_hash_table_);
-    set.Insert(function);
-    StorePointer(&raw_ptr()->functions_hash_table_, set.Release().raw());
-  }
+  SetFunctions(new_arr);
 }
 
 
@@ -2650,6 +2596,7 @@
   }
 
   virtual void UpdateArrayTo(const Array& value) {
+    // TODO(fschneider): Fails for classes in the VM isolate.
     cls_.set_cha_codes(value);
   }
 
@@ -3884,15 +3831,6 @@
   ASSERT(!funcs.IsNull());
   const intptr_t len = funcs.Length();
   Function& function = isolate->FunctionHandle();
-  if (len >= kFunctionLookupHashTreshold) {
-    ClassFunctionsSet set(raw_ptr()->functions_hash_table_);
-    REUSABLE_STRING_HANDLESCOPE(isolate);
-    function ^= set.GetOrNull(FunctionName(name, &(isolate->StringHandle())));
-    // No mutations.
-    ASSERT(set.Release().raw() == raw_ptr()->functions_hash_table_);
-    return function.IsNull() ? Function::null()
-                             : CheckFunctionType(function, kind);
-  }
   if (name.IsSymbol()) {
     // Quick Symbol compare.
     NoGCScope no_gc;
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index bb4d0ac..892060d 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -949,7 +949,6 @@
   // Returns true if non-static fields are defined.
   bool HasInstanceFields() const;
 
-  // TODO(koda): Avoid VM service indexing into this array; unite w/ hash table.
   RawArray* functions() const { return raw_ptr()->functions_; }
   void SetFunctions(const Array& value) const;
   void AddFunction(const Function& function) const;
@@ -1212,9 +1211,6 @@
 
   void CalculateFieldOffsets() const;
 
-  // functions_hash_table is in use iff there are at least this many functions.
-  static const intptr_t kFunctionLookupHashTreshold = 16;
-
   // Initial value for the cached number of type arguments.
   static const intptr_t kUnknownNumTypeArguments = -1;
 
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 86db68a..bff2eff 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -499,7 +499,6 @@
   RawString* name_;
   RawString* user_name_;
   RawArray* functions_;
-  RawArray* functions_hash_table_;
   RawArray* fields_;
   RawArray* offset_in_words_to_field_;
   RawGrowableObjectArray* closure_functions_;  // Local functions and literals.
diff --git a/tests/language/cha_deopt1_deferred_lib.dart b/tests/language/cha_deopt1_deferred_lib.dart
new file mode 100644
index 0000000..fc07315
--- /dev/null
+++ b/tests/language/cha_deopt1_deferred_lib.dart
@@ -0,0 +1,13 @@
+// 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 "cha_deopt1_lib.dart";
+
+class U extends T {
+
+  m() => "good horse";
+
+}
+
+make_u() => new U();
diff --git a/tests/language/cha_deopt1_lib.dart b/tests/language/cha_deopt1_lib.dart
new file mode 100644
index 0000000..f49562d
--- /dev/null
+++ b/tests/language/cha_deopt1_lib.dart
@@ -0,0 +1,12 @@
+// 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.
+
+library mylib;
+
+class T {
+
+  m() => 42;
+
+}
+
diff --git a/tests/language/cha_deopt1_test.dart b/tests/language/cha_deopt1_test.dart
new file mode 100644
index 0000000..fd860bf
--- /dev/null
+++ b/tests/language/cha_deopt1_test.dart
@@ -0,0 +1,50 @@
+// 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.
+// VMOptions=--optimization-counter-threshold=100
+
+// Test lazy deoptimization at field guards with deferred loading.
+
+import "package:expect/expect.dart";
+import "cha_deopt1_lib.dart";
+import "cha_deopt1_deferred_lib.dart" deferred as d;
+
+var loaded = false;
+
+main() {
+  for (var i = 0; i < 2000; i++) bla();
+  Expect.equals(42, bla());
+  d.loadLibrary().then((_) {
+    loaded = true;
+    Expect.equals("good horse", bla());
+  });
+}
+
+make_t() {
+  try {
+    if (loaded) {
+      return d.make_u();
+    } else {
+      return new T();
+    }
+  } catch (e) { }
+}
+
+bla() {
+  var x = new X();
+  x.test(make_t());
+  return x.fld.m();
+}
+
+
+class X {
+  T fld = new T();
+
+  test(T t) {
+    if (t != null) {
+      T tmp = t;
+      fld = tmp;
+    }
+  }
+}
+
diff --git a/tests/language/cha_deopt2_deferred_lib.dart b/tests/language/cha_deopt2_deferred_lib.dart
new file mode 100644
index 0000000..b1ef444
--- /dev/null
+++ b/tests/language/cha_deopt2_deferred_lib.dart
@@ -0,0 +1,9 @@
+// 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 "cha_deopt2_lib.dart";
+
+class U extends T { }
+
+make_u() => new U();
diff --git a/tests/language/cha_deopt2_lib.dart b/tests/language/cha_deopt2_lib.dart
new file mode 100644
index 0000000..3af6575
--- /dev/null
+++ b/tests/language/cha_deopt2_lib.dart
@@ -0,0 +1,13 @@
+// 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.
+
+library mylib;
+
+class A { }
+class B { }
+class C { }
+class D { }
+class E { }
+class T { }
+
diff --git a/tests/language/cha_deopt2_test.dart b/tests/language/cha_deopt2_test.dart
new file mode 100644
index 0000000..5a4ed04
--- /dev/null
+++ b/tests/language/cha_deopt2_test.dart
@@ -0,0 +1,39 @@
+// 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.
+// VMOptions=--optimization-counter-threshold=100
+
+// Test lazy deoptimization at type checks with deferred loading.
+
+import "package:expect/expect.dart";
+import "cha_deopt2_lib.dart";
+import "cha_deopt2_deferred_lib.dart" deferred as d;
+
+var loaded = false;
+
+main() {
+  for (var i = 0; i < 2000; i++) bla();
+  Expect.equals(1, bla());
+  d.loadLibrary().then((_) {
+    loaded = true;
+    Expect.equals(1, bla());
+  });
+}
+
+make_array() {
+  try {
+    if (loaded) {
+      return [new A(), new B(), new C(), new D(), new E(), d.make_u()];
+    } else {
+      return [new A(), new B(), new C(), new D(), new E(), new T()];
+    }
+  } catch (e) { }
+}
+
+bla() {
+  var count = 0;
+  for (var x in make_array()) {
+    if (x is T) count++;
+  }
+  return count;
+}
diff --git a/tests/language/cha_deopt3_deferred_lib.dart b/tests/language/cha_deopt3_deferred_lib.dart
new file mode 100644
index 0000000..f9cffb1
--- /dev/null
+++ b/tests/language/cha_deopt3_deferred_lib.dart
@@ -0,0 +1,9 @@
+// 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 "cha_deopt3_lib.dart";
+
+class U implements T { }
+
+make_u() => new U();
diff --git a/tests/language/cha_deopt3_lib.dart b/tests/language/cha_deopt3_lib.dart
new file mode 100644
index 0000000..3af6575
--- /dev/null
+++ b/tests/language/cha_deopt3_lib.dart
@@ -0,0 +1,13 @@
+// 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.
+
+library mylib;
+
+class A { }
+class B { }
+class C { }
+class D { }
+class E { }
+class T { }
+
diff --git a/tests/language/cha_deopt3_test.dart b/tests/language/cha_deopt3_test.dart
new file mode 100644
index 0000000..3dc61b9
--- /dev/null
+++ b/tests/language/cha_deopt3_test.dart
@@ -0,0 +1,39 @@
+// 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.
+// VMOptions=--optimization-counter-threshold=100
+
+// Test lazy deoptimization at type checks with interface implementation.
+
+import "package:expect/expect.dart";
+import "cha_deopt3_lib.dart";
+import "cha_deopt3_deferred_lib.dart" deferred as d;
+
+var loaded = false;
+
+main() {
+  for (var i = 0; i < 2000; i++) bla();
+  Expect.equals(1, bla());
+  d.loadLibrary().then((_) {
+    loaded = true;
+    Expect.equals(1, bla());
+  });
+}
+
+make_array() {
+  try {
+    if (loaded) {
+      return [new A(), new B(), new C(), new D(), new E(), d.make_u()];
+    } else {
+      return [new A(), new B(), new C(), new D(), new E(), new T()];
+    }
+  } catch (e) { }
+}
+
+bla() {
+  var count = 0;
+  for (var x in make_array()) {
+    if (x is T) count++;
+  }
+  return count;
+}
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index 7c9aa07..498301e 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -245,6 +245,9 @@
 new_expression_type_args_test/01: Fail # Wrongly reports compile-time error.
 
 deferred*: skip # Issue 17523
+cha_deopt1_test: skip # Issue 17523
+cha_deopt2_test: skip # Issue 17523
+cha_deopt3_test: skip # Issue 17523
 
 [ $compiler == dart2dart && $minified ]
 cyclic_type_test/0*: Fail # Issue 12605.
diff --git a/tests/standalone/issue14236_test.dart b/tests/standalone/issue14236_test.dart
index 7e248f3..f720a65 100644
--- a/tests/standalone/issue14236_test.dart
+++ b/tests/standalone/issue14236_test.dart
Binary files differ
diff --git a/tools/VERSION b/tools/VERSION
index 4d548ae..938a641 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 6
 PATCH 0
 PRERELEASE 9
-PRERELEASE_PATCH 1
+PRERELEASE_PATCH 2