Merge "Quick compiler: disable GVN DO NOT MERGE" into lmp-dev
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 491d72e..5817f92 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -726,6 +726,8 @@
 
   int64_t ConstantValueWide(RegLocation loc) const {
     DCHECK(IsConst(loc));
+    DCHECK(!loc.high_word);  // Do not allow asking for the high partner.
+    DCHECK_LT(loc.orig_sreg + 1, GetNumSSARegs());
     return (static_cast<int64_t>(constant_values_[loc.orig_sreg + 1]) << 32) |
         Low32Bits(static_cast<int64_t>(constant_values_[loc.orig_sreg]));
   }
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 2a51b49..ee1c467 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -56,16 +56,23 @@
   bool res = false;
   if (rl_src.is_const) {
     if (rl_src.wide) {
+      // For wide registers, check whether we're the high partner. In that case we need to switch
+      // to the lower one for the correct value.
+      if (rl_src.high_word) {
+        rl_src.high_word = false;
+        rl_src.s_reg_low--;
+        rl_src.orig_sreg--;
+      }
       if (rl_src.fp) {
-         res = InexpensiveConstantDouble(mir_graph_->ConstantValueWide(rl_src));
+        res = InexpensiveConstantDouble(mir_graph_->ConstantValueWide(rl_src));
       } else {
-         res = InexpensiveConstantLong(mir_graph_->ConstantValueWide(rl_src));
+        res = InexpensiveConstantLong(mir_graph_->ConstantValueWide(rl_src));
       }
     } else {
       if (rl_src.fp) {
-         res = InexpensiveConstantFloat(mir_graph_->ConstantValue(rl_src));
+        res = InexpensiveConstantFloat(mir_graph_->ConstantValue(rl_src));
       } else {
-         res = InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src));
+        res = InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src));
       }
     }
   }
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index c1610c2..91c7aec 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1457,51 +1457,6 @@
   DISALLOW_COPY_AND_ASSIGN(ParallelCompilationManager);
 };
 
-static bool SkipClassCheckClassPath(const char* descriptor, const DexFile& dex_file,
-                                    const std::vector<const DexFile*>& classpath) {
-  DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, classpath);
-  CHECK(pair.second != NULL);
-  if (pair.first != &dex_file) {
-    LOG(WARNING) << "Skipping class " << descriptor << " from " << dex_file.GetLocation()
-                 << " previously found in " << pair.first->GetLocation();
-    return true;
-  }
-  return false;
-}
-
-// Return true if the class should be skipped during compilation.
-//
-// The first case where we skip is for redundant class definitions in
-// the boot classpath. We skip all but the first definition in that case.
-//
-// The second case where we skip is when an app bundles classes found
-// in the boot classpath. Since at runtime we will select the class from
-// the boot classpath, we ignore the one from the app.
-//
-// The third case is if the app itself has the class defined in multiple dex files. Then we skip
-// it if it is not the first occurrence.
-static bool SkipClass(ClassLinker* class_linker, jobject class_loader, const DexFile& dex_file,
-                      const std::vector<const DexFile*>& dex_files,
-                      const DexFile::ClassDef& class_def) {
-  const char* descriptor = dex_file.GetClassDescriptor(class_def);
-
-  if (class_loader == NULL) {
-    return SkipClassCheckClassPath(descriptor, dex_file, class_linker->GetBootClassPath());
-  }
-
-  if (class_linker->IsInBootClassPath(descriptor)) {
-    return true;
-  }
-
-  if (dex_files.size() > 1) {
-    // Multi-dex compilation, only take first class.
-    return SkipClassCheckClassPath(descriptor, dex_file, dex_files);
-  } else {
-    // Single dex, take everything.
-    return false;
-  }
-}
-
 // A fast version of SkipClass above if the class pointer is available
 // that avoids the expensive FindInClassPath search.
 static bool SkipClass(jobject class_loader, const DexFile& dex_file, mirror::Class* klass)
@@ -1568,81 +1523,84 @@
   // definitions, since many of them many never be referenced by
   // generated code.
   const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
-  if (!SkipClass(class_linker, jclass_loader, dex_file, manager->GetDexFiles(), class_def)) {
-    ScopedObjectAccess soa(self);
-    StackHandleScope<2> hs(soa.Self());
-    Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
-    // Resolve the class.
-    mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
-                                                     class_loader);
-    bool resolve_fields_and_methods;
-    if (klass == NULL) {
-      // Class couldn't be resolved, for example, super-class is in a different dex file. Don't
-      // attempt to resolve methods and fields when there is no declaring class.
-      CheckAndClearResolveException(soa.Self());
-      resolve_fields_and_methods = false;
-    } else {
-      resolve_fields_and_methods = manager->GetCompiler()->IsImage();
+  ScopedObjectAccess soa(self);
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
+  // Resolve the class.
+  mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
+                                                   class_loader);
+  bool resolve_fields_and_methods;
+  if (klass == nullptr) {
+    // Class couldn't be resolved, for example, super-class is in a different dex file. Don't
+    // attempt to resolve methods and fields when there is no declaring class.
+    CheckAndClearResolveException(soa.Self());
+    resolve_fields_and_methods = false;
+  } else {
+    // We successfully resolved a class, should we skip it?
+    if (SkipClass(jclass_loader, dex_file, klass)) {
+      return;
     }
-    // Note the class_data pointer advances through the headers,
-    // static fields, instance fields, direct methods, and virtual
-    // methods.
-    const byte* class_data = dex_file.GetClassData(class_def);
-    if (class_data == NULL) {
-      // Empty class such as a marker interface.
-      requires_constructor_barrier = false;
-    } else {
-      ClassDataItemIterator it(dex_file, class_data);
-      while (it.HasNextStaticField()) {
-        if (resolve_fields_and_methods) {
-          mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
-                                                               dex_cache, class_loader, true);
-          if (field == NULL) {
-            CheckAndClearResolveException(soa.Self());
-          }
+    // We want to resolve the methods and fields eagerly.
+    resolve_fields_and_methods = true;
+  }
+  // Note the class_data pointer advances through the headers,
+  // static fields, instance fields, direct methods, and virtual
+  // methods.
+  const byte* class_data = dex_file.GetClassData(class_def);
+  if (class_data == NULL) {
+    // Empty class such as a marker interface.
+    requires_constructor_barrier = false;
+  } else {
+    ClassDataItemIterator it(dex_file, class_data);
+    while (it.HasNextStaticField()) {
+      if (resolve_fields_and_methods) {
+        mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+                                                             dex_cache, class_loader, true);
+        if (field == NULL) {
+          CheckAndClearResolveException(soa.Self());
         }
-        it.Next();
       }
-      // We require a constructor barrier if there are final instance fields.
-      requires_constructor_barrier = false;
-      while (it.HasNextInstanceField()) {
-        if ((it.GetMemberAccessFlags() & kAccFinal) != 0) {
-          requires_constructor_barrier = true;
-        }
-        if (resolve_fields_and_methods) {
-          mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
-                                                               dex_cache, class_loader, false);
-          if (field == NULL) {
-            CheckAndClearResolveException(soa.Self());
-          }
-        }
-        it.Next();
+      it.Next();
+    }
+    // We require a constructor barrier if there are final instance fields.
+    requires_constructor_barrier = false;
+    while (it.HasNextInstanceField()) {
+      if ((it.GetMemberAccessFlags() & kAccFinal) != 0) {
+        requires_constructor_barrier = true;
       }
       if (resolve_fields_and_methods) {
-        while (it.HasNextDirectMethod()) {
-          mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
-                                                                  dex_cache, class_loader,
-                                                                  NullHandle<mirror::ArtMethod>(),
-                                                                  it.GetMethodInvokeType(class_def));
-          if (method == NULL) {
-            CheckAndClearResolveException(soa.Self());
-          }
-          it.Next();
+        mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+                                                             dex_cache, class_loader, false);
+        if (field == NULL) {
+          CheckAndClearResolveException(soa.Self());
         }
-        while (it.HasNextVirtualMethod()) {
-          mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
-                                                                  dex_cache, class_loader,
-                                                                  NullHandle<mirror::ArtMethod>(),
-                                                                  it.GetMethodInvokeType(class_def));
-          if (method == NULL) {
-            CheckAndClearResolveException(soa.Self());
-          }
-          it.Next();
-        }
-        DCHECK(!it.HasNext());
       }
+      it.Next();
+    }
+    if (resolve_fields_and_methods) {
+      while (it.HasNextDirectMethod()) {
+        mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
+                                                                dex_cache, class_loader,
+                                                                NullHandle<mirror::ArtMethod>(),
+                                                                it.GetMethodInvokeType(class_def));
+        if (method == NULL) {
+          CheckAndClearResolveException(soa.Self());
+        }
+        it.Next();
+      }
+      while (it.HasNextVirtualMethod()) {
+        mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
+                                                                dex_cache, class_loader,
+                                                                NullHandle<mirror::ArtMethod>(),
+                                                                it.GetMethodInvokeType(class_def));
+        if (method == NULL) {
+          CheckAndClearResolveException(soa.Self());
+        }
+        it.Next();
+      }
+      DCHECK(!it.HasNext());
     }
   }
   if (requires_constructor_barrier) {
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 75bc49b..c1fb267 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -916,7 +916,7 @@
     } else if (type->IsStringClass()) {
       mirror::String* string = value->AsString();
       os << StringPrintf("%p   String: %s\n", string,
-                         PrintableString(string->ToModifiedUtf8()).c_str());
+                         PrintableString(string->ToModifiedUtf8().c_str()).c_str());
     } else if (type->IsClassClass()) {
       mirror::Class* klass = value->AsClass();
       os << StringPrintf("%p   Class: %s\n", klass, PrettyDescriptor(klass).c_str());
@@ -1049,7 +1049,7 @@
                          PrettyMethod(obj->AsArtMethod()).c_str());
     } else if (obj_class->IsStringClass()) {
       os << StringPrintf("%p: java.lang.String %s\n", obj,
-                         PrintableString(obj->AsString()->ToModifiedUtf8()).c_str());
+                         PrintableString(obj->AsString()->ToModifiedUtf8().c_str()).c_str());
     } else {
       os << StringPrintf("%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
     }
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 3af90b2..d05f7af 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -29,11 +29,6 @@
 
 namespace art {
 
-inline bool ClassLinker::IsInBootClassPath(const char* descriptor) {
-  DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
-  return pair.second != nullptr;
-}
-
 inline mirror::Class* ClassLinker::FindSystemClass(Thread* self, const char* descriptor) {
   return FindClass(self, descriptor, NullHandle<mirror::ClassLoader>());
 }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 51ee448..27483e7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1899,6 +1899,23 @@
   return klass;
 }
 
+typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry;
+
+// Search a collection of DexFiles for a descriptor
+ClassPathEntry FindInClassPath(const char* descriptor,
+                               const std::vector<const DexFile*>& class_path) {
+  for (size_t i = 0; i != class_path.size(); ++i) {
+    const DexFile* dex_file = class_path[i];
+    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+    if (dex_class_def != NULL) {
+      return ClassPathEntry(dex_file, dex_class_def);
+    }
+  }
+  // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
+  return ClassPathEntry(reinterpret_cast<const DexFile*>(NULL),
+                        reinterpret_cast<const DexFile::ClassDef*>(NULL));
+}
+
 mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
                                       Handle<mirror::ClassLoader> class_loader) {
   DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
@@ -1911,25 +1928,40 @@
   }
   // Find the class in the loaded classes table.
   mirror::Class* klass = LookupClass(descriptor, class_loader.Get());
-  if (klass != NULL) {
+  if (klass != nullptr) {
     return EnsureResolved(self, descriptor, klass);
   }
   // Class is not yet loaded.
   if (descriptor[0] == '[') {
     return CreateArrayClass(self, descriptor, class_loader);
   } else if (class_loader.Get() == nullptr) {
-    DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
-    if (pair.second != NULL) {
+    // The boot class loader, search the boot class path.
+    ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+    if (pair.second != nullptr) {
       StackHandleScope<1> hs(self);
       return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
+    } else {
+      // The boot class loader is searched ahead of the application class loader, failures are
+      // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
+      // trigger the chaining with a proper stack trace.
+      mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+      self->SetException(ThrowLocation(), pre_allocated);
+      return nullptr;
     }
   } else if (Runtime::Current()->UseCompileTimeClassPath()) {
-    // First try the boot class path, we check the descriptor first to avoid an unnecessary
-    // throw of a NoClassDefFoundError.
-    if (IsInBootClassPath(descriptor)) {
-      mirror::Class* system_class = FindSystemClass(self, descriptor);
-      CHECK(system_class != NULL);
-      return system_class;
+    // First try with the bootstrap class loader.
+    if (class_loader.Get() != nullptr) {
+      klass = LookupClass(descriptor, nullptr);
+      if (klass != nullptr) {
+        return EnsureResolved(self, descriptor, klass);
+      }
+    }
+    // If the lookup failed search the boot class path. We don't perform a recursive call to avoid
+    // a NoClassDefFoundError being allocated.
+    ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+    if (pair.second != nullptr) {
+      StackHandleScope<1> hs(self);
+      return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
     }
     // Next try the compile time class path.
     const std::vector<const DexFile*>* class_path;
@@ -1939,38 +1971,37 @@
                                             soa.AddLocalReference<jobject>(class_loader.Get()));
       class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
     }
-
-    DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, *class_path);
-    if (pair.second != NULL) {
+    pair = FindInClassPath(descriptor, *class_path);
+    if (pair.second != nullptr) {
       return DefineClass(descriptor, class_loader, *pair.first, *pair.second);
     }
-
   } else {
     ScopedObjectAccessUnchecked soa(self);
     ScopedLocalRef<jobject> class_loader_object(soa.Env(),
                                                 soa.AddLocalReference<jobject>(class_loader.Get()));
     std::string class_name_string(DescriptorToDot(descriptor));
-    ScopedLocalRef<jobject> result(soa.Env(), NULL);
+    ScopedLocalRef<jobject> result(soa.Env(), nullptr);
     {
       ScopedThreadStateChange tsc(self, kNative);
       ScopedLocalRef<jobject> class_name_object(soa.Env(),
                                                 soa.Env()->NewStringUTF(class_name_string.c_str()));
-      if (class_name_object.get() == NULL) {
-        return NULL;
+      if (class_name_object.get() == nullptr) {
+        DCHECK(self->IsExceptionPending());  // OOME.
+        return nullptr;
       }
-      CHECK(class_loader_object.get() != NULL);
+      CHECK(class_loader_object.get() != nullptr);
       result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
                                                WellKnownClasses::java_lang_ClassLoader_loadClass,
                                                class_name_object.get()));
     }
     if (self->IsExceptionPending()) {
       // If the ClassLoader threw, pass that exception up.
-      return NULL;
-    } else if (result.get() == NULL) {
+      return nullptr;
+    } else if (result.get() == nullptr) {
       // broken loader - throw NPE to be compatible with Dalvik
-      ThrowNullPointerException(NULL, StringPrintf("ClassLoader.loadClass returned null for %s",
-                                                   class_name_string.c_str()).c_str());
-      return NULL;
+      ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s",
+                                                      class_name_string.c_str()).c_str());
+      return nullptr;
     } else {
       // success, return mirror::Class*
       return soa.Decode<mirror::Class*>(result.get());
@@ -1978,7 +2009,7 @@
   }
 
   ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
-  return NULL;
+  return nullptr;
 }
 
 mirror::Class* ClassLinker::DefineClass(const char* descriptor,
@@ -3084,7 +3115,7 @@
       // Searching the image dex files/caches failed, we don't want to get into this situation
       // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image
       // classes into the class table.
-      const int32_t kMaxFailedDexCacheLookups = 1000;
+      constexpr uint32_t kMaxFailedDexCacheLookups = 1000;
       if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) {
         MoveImageClassesToClassTable();
       }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index bf2e781..b68b23e 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -68,8 +68,6 @@
   // Initialize class linker from one or more images.
   void InitFromImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsInBootClassPath(const char* descriptor);
-
   // Finds a class by its descriptor, loading it if necessary.
   // If class_loader is null, searches boot_class_path_.
   mirror::Class* FindClass(Thread* self, const char* descriptor,
@@ -85,7 +83,7 @@
   mirror::Class* FindArrayClass(Thread* self, mirror::Class** element_class)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Reutrns true if the class linker is initialized.
+  // Returns true if the class linker is initialized.
   bool IsInitialized() const;
 
   // Define a new a class based on a ClassDef from a DexFile
@@ -638,7 +636,7 @@
   bool dex_cache_image_class_lookup_required_;
   // Number of times we've searched dex caches for a class. After a certain number of misses we move
   // the classes into the class_table_ to avoid dex cache based searches.
-  AtomicInteger failed_dex_cache_class_lookups_;
+  Atomic<uint32_t> failed_dex_cache_class_lookups_;
 
   mirror::Class* LookupClassFromTableLocked(const char* descriptor,
                                             const mirror::ClassLoader* class_loader,
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index ddc2147..38ea754 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -50,20 +50,6 @@
 const byte DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
 const byte DexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' };
 
-DexFile::ClassPathEntry DexFile::FindInClassPath(const char* descriptor,
-                                                 const ClassPath& class_path) {
-  for (size_t i = 0; i != class_path.size(); ++i) {
-    const DexFile* dex_file = class_path[i];
-    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
-    if (dex_class_def != NULL) {
-      return ClassPathEntry(dex_file, dex_class_def);
-    }
-  }
-  // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
-  return ClassPathEntry(reinterpret_cast<const DexFile*>(NULL),
-                        reinterpret_cast<const DexFile::ClassDef*>(NULL));
-}
-
 static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) {
   CHECK(magic != NULL);
   ScopedFd fd(open(filename, O_RDONLY, 0));
@@ -366,7 +352,10 @@
       field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)),
       method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
       proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
-      class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)) {
+      class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
+      find_class_def_misses_(0),
+      class_def_index_(nullptr),
+      build_class_def_index_mutex_("DexFile index creation mutex") {
   CHECK(begin_ != NULL) << GetLocation();
   CHECK_GT(size_, 0U) << GetLocation();
 }
@@ -376,6 +365,8 @@
   // that's only called after DetachCurrentThread, which means there's no JNIEnv. We could
   // re-attach, but cleaning up these global references is not obviously useful. It's not as if
   // the global reference table is otherwise empty!
+  // Remove the index if one were created.
+  delete class_def_index_.LoadRelaxed();
 }
 
 bool DexFile::Init(std::string* error_msg) {
@@ -424,26 +415,51 @@
 }
 
 const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor) const {
-  size_t num_class_defs = NumClassDefs();
+  // If we have an index lookup the descriptor via that as its constant time to search.
+  Index* index = class_def_index_.LoadSequentiallyConsistent();
+  if (index != nullptr) {
+    auto it = index->find(descriptor);
+    return (it == index->end()) ? nullptr : it->second;
+  }
+  // Fast path for rate no class defs case.
+  uint32_t num_class_defs = NumClassDefs();
   if (num_class_defs == 0) {
-    return NULL;
+    return nullptr;
   }
+  // Search for class def with 2 binary searches and then a linear search.
   const StringId* string_id = FindStringId(descriptor);
-  if (string_id == NULL) {
-    return NULL;
-  }
-  const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id));
-  if (type_id == NULL) {
-    return NULL;
-  }
-  uint16_t type_idx = GetIndexForTypeId(*type_id);
-  for (size_t i = 0; i < num_class_defs; ++i) {
-    const ClassDef& class_def = GetClassDef(i);
-    if (class_def.class_idx_ == type_idx) {
-      return &class_def;
+  if (string_id != nullptr) {
+    const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id));
+    if (type_id != nullptr) {
+      uint16_t type_idx = GetIndexForTypeId(*type_id);
+      for (size_t i = 0; i < num_class_defs; ++i) {
+        const ClassDef& class_def = GetClassDef(i);
+        if (class_def.class_idx_ == type_idx) {
+          return &class_def;
+        }
+      }
     }
   }
-  return NULL;
+  // A miss. If we've had kMaxFailedDexClassDefLookups misses then build an index to speed things
+  // up. This isn't done eagerly at construction as construction is not performed in multi-threaded
+  // sections of tools like dex2oat. If we're lazy we hopefully increase the chance of balancing
+  // out which thread builds the index.
+  find_class_def_misses_++;
+  const uint32_t kMaxFailedDexClassDefLookups = 100;
+  if (find_class_def_misses_ > kMaxFailedDexClassDefLookups) {
+    MutexLock mu(Thread::Current(), build_class_def_index_mutex_);
+    // Are we the first ones building the index?
+    if (class_def_index_.LoadSequentiallyConsistent() == nullptr) {
+      index = new Index(num_class_defs);
+      for (uint32_t i = 0; i < num_class_defs;  ++i) {
+        const ClassDef& class_def = GetClassDef(i);
+        const char* descriptor = GetClassDescriptor(class_def);
+        index->insert(std::make_pair(descriptor, &class_def));
+      }
+      class_def_index_.StoreSequentiallyConsistent(index);
+    }
+  }
+  return nullptr;
 }
 
 const DexFile::ClassDef* DexFile::FindClassDef(uint16_t type_idx) const {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index a71a5e1..1da1f81 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -19,6 +19,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/logging.h"
@@ -27,7 +28,7 @@
 #include "invoke_type.h"
 #include "jni.h"
 #include "modifiers.h"
-#include "safe_map.h"
+#include "utf.h"
 
 namespace art {
 
@@ -356,13 +357,6 @@
     DISALLOW_COPY_AND_ASSIGN(AnnotationItem);
   };
 
-  typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry;
-  typedef std::vector<const DexFile*> ClassPath;
-
-  // Search a collection of DexFiles for a descriptor
-  static ClassPathEntry FindInClassPath(const char* descriptor,
-                                        const ClassPath& class_path);
-
   // Returns the checksum of a file for comparison with GetLocationChecksum().
   // For .dex files, this is the header checksum.
   // For zip files, this is the classes.dex zip entry CRC32 checksum.
@@ -478,7 +472,7 @@
   const StringId* FindStringId(const uint16_t* string) const;
 
   // Returns the number of type identifiers in the .dex file.
-  size_t NumTypeIds() const {
+  uint32_t NumTypeIds() const {
     DCHECK(header_ != NULL) << GetLocation();
     return header_->type_ids_size_;
   }
@@ -607,7 +601,7 @@
     return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
   }
   // Returns the number of class definitions in the .dex file.
-  size_t NumClassDefs() const {
+  uint32_t NumClassDefs() const {
     DCHECK(header_ != NULL) << GetLocation();
     return header_->class_defs_size_;
   }
@@ -969,6 +963,23 @@
 
   // Points to the base of the class definition list.
   const ClassDef* const class_defs_;
+
+  // Number of misses finding a class def from a descriptor.
+  mutable Atomic<uint32_t> find_class_def_misses_;
+
+  struct UTF16HashCmp {
+    // Hash function.
+    size_t operator()(const char* key) const {
+      return ComputeUtf8Hash(key);
+    }
+    // std::equal function.
+    bool operator()(const char* a, const char* b) const {
+      return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(a, b) == 0;
+    }
+  };
+  typedef std::unordered_map<const char*, const ClassDef*, UTF16HashCmp, UTF16HashCmp> Index;
+  mutable Atomic<Index*> class_def_index_;
+  mutable Mutex build_class_def_index_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
 };
 std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
 
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index d7d541a..976cac9 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -171,7 +171,7 @@
 }
 
 bool DexFileVerifier::CheckListSize(const void* start, size_t count, size_t elem_size,
-                                        const char* label) {
+                                    const char* label) {
   // Check that size is not 0.
   CHECK_NE(elem_size, 0U);
 
@@ -201,6 +201,23 @@
   return true;
 }
 
+bool DexFileVerifier::CheckList(size_t element_size, const char* label, const byte* *ptr) {
+  // Check that the list is available. The first 4B are the count.
+  if (!CheckListSize(*ptr, 1, 4U, label)) {
+    return false;
+  }
+
+  uint32_t count = *reinterpret_cast<const uint32_t*>(*ptr);
+  if (count > 0) {
+    if (!CheckListSize(*ptr + 4, count, element_size, label)) {
+      return false;
+    }
+  }
+
+  *ptr += 4 + count * element_size;
+  return true;
+}
+
 bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) {
   if (UNLIKELY(field >= limit)) {
     ErrorStringPrintf("Bad index for %s: %x >= %x", label, field, limit);
@@ -209,6 +226,20 @@
   return true;
 }
 
+bool DexFileVerifier::CheckValidOffsetAndSize(uint32_t offset, uint32_t size, const char* label) {
+  if (size == 0) {
+    if (offset != 0) {
+      ErrorStringPrintf("Offset(%d) should be zero when size is zero for %s.", offset, label);
+      return false;
+    }
+  }
+  if (size_ <= offset) {
+    ErrorStringPrintf("Offset(%d) should be within file size(%zu) for %s.", offset, size_, label);
+    return false;
+  }
+  return true;
+}
+
 bool DexFileVerifier::CheckHeader() {
   // Check file size from the header.
   uint32_t expected_size = header_->file_size_;
@@ -238,11 +269,29 @@
     return false;
   }
 
-  return true;
+  // Check that all offsets are inside the file.
+  bool result =
+      CheckValidOffsetAndSize(header_->link_off_, header_->link_size_, "link") &&
+      CheckValidOffsetAndSize(header_->map_off_, header_->map_off_, "map") &&
+      CheckValidOffsetAndSize(header_->string_ids_off_, header_->string_ids_size_, "string-ids") &&
+      CheckValidOffsetAndSize(header_->type_ids_off_, header_->type_ids_size_, "type-ids") &&
+      CheckValidOffsetAndSize(header_->proto_ids_off_, header_->proto_ids_size_, "proto-ids") &&
+      CheckValidOffsetAndSize(header_->field_ids_off_, header_->field_ids_size_, "field-ids") &&
+      CheckValidOffsetAndSize(header_->method_ids_off_, header_->method_ids_size_, "method-ids") &&
+      CheckValidOffsetAndSize(header_->class_defs_off_, header_->class_defs_size_, "class-defs") &&
+      CheckValidOffsetAndSize(header_->data_off_, header_->data_size_, "data");
+
+  return result;
 }
 
 bool DexFileVerifier::CheckMap() {
-  const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
+  const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ +
+                                                                          header_->map_off_);
+  // Check that map list content is available.
+  if (!CheckListSize(map, 1, sizeof(DexFile::MapList), "maplist content")) {
+    return false;
+  }
+
   const DexFile::MapItem* item = map->list_;
 
   uint32_t count = map->size_;
@@ -1117,48 +1166,21 @@
         break;
       }
       case DexFile::kDexTypeTypeList: {
-        const DexFile::TypeList* list = reinterpret_cast<const DexFile::TypeList*>(ptr_);
-
-        // Check that at least the header (size) is available.
-        if (!CheckListSize(list, 1, DexFile::TypeList::GetHeaderSize(), "type_list")) {
+        if (!CheckList(sizeof(DexFile::TypeItem), "type_list", &ptr_)) {
           return false;
         }
-
-        // Check that the whole list is available.
-        const size_t list_size = DexFile::TypeList::GetListSize(list->Size());
-        if (!CheckListSize(list, 1, list_size, "type_list size")) {
-          return false;
-        }
-
-        ptr_ += list_size;
         break;
       }
       case DexFile::kDexTypeAnnotationSetRefList: {
-        const DexFile::AnnotationSetRefList* list =
-            reinterpret_cast<const DexFile::AnnotationSetRefList*>(ptr_);
-        const DexFile::AnnotationSetRefItem* item = list->list_;
-        uint32_t count = list->size_;
-
-        if (!CheckListSize(list, 1, sizeof(DexFile::AnnotationSetRefList),
-                               "annotation_set_ref_list") ||
-            !CheckListSize(item, count, sizeof(DexFile::AnnotationSetRefItem),
-                           "annotation_set_ref_list size")) {
+        if (!CheckList(sizeof(DexFile::AnnotationSetRefItem), "annotation_set_ref_list", &ptr_)) {
           return false;
         }
-        ptr_ = reinterpret_cast<const byte*>(item + count);
         break;
       }
       case DexFile::kDexTypeAnnotationSetItem: {
-        const DexFile::AnnotationSetItem* set =
-            reinterpret_cast<const DexFile::AnnotationSetItem*>(ptr_);
-        const uint32_t* item = set->entries_;
-        uint32_t count = set->size_;
-
-        if (!CheckListSize(set, 1, sizeof(DexFile::AnnotationSetItem), "annotation_set_item") ||
-            !CheckListSize(item, count, sizeof(uint32_t), "annotation_set_item size")) {
+        if (!CheckList(sizeof(uint32_t), "annotation_set_item", &ptr_)) {
           return false;
         }
-        ptr_ = reinterpret_cast<const byte*>(item + count);
         break;
       }
       case DexFile::kDexTypeClassDataItem: {
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 1d915b9..606da54 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -43,6 +43,12 @@
 
   bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, bool is_return_type);
   bool CheckListSize(const void* start, size_t count, size_t element_size, const char* label);
+  // Check a list. The head is assumed to be at *ptr, and elements to be of size element_size. If
+  // successful, the ptr will be moved forward the amount covered by the list.
+  bool CheckList(size_t element_size, const char* label, const byte* *ptr);
+  // Checks whether the offset is zero (when size is zero) or that the offset falls within the area
+  // claimed by the file.
+  bool CheckValidOffsetAndSize(uint32_t offset, uint32_t size, const char* label);
   bool CheckIndex(uint32_t field, uint32_t limit, const char* label);
 
   bool CheckHeader();
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 8d614e1..ce914e5 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -138,7 +138,6 @@
       growth_limit_(growth_limit),
       max_allowed_footprint_(initial_size),
       native_footprint_gc_watermark_(initial_size),
-      native_footprint_limit_(2 * initial_size),
       native_need_to_run_finalization_(false),
       // Initially assume we perceive jank in case the process state is never updated.
       process_state_(kProcessStateJankPerceptible),
@@ -2833,7 +2832,6 @@
     target_size = native_size + min_free_;
   }
   native_footprint_gc_watermark_ = target_size;
-  native_footprint_limit_ = 2 * target_size - native_size;
 }
 
 collector::GarbageCollector* Heap::FindCollectorByGcType(collector::GcType gc_type) {
@@ -3105,14 +3103,14 @@
 
     // The second watermark is higher than the gc watermark. If you hit this it means you are
     // allocating native objects faster than the GC can keep up with.
-    if (new_native_bytes_allocated > native_footprint_limit_) {
+    if (new_native_bytes_allocated > growth_limit_) {
       if (WaitForGcToComplete(kGcCauseForNativeAlloc, self) != collector::kGcTypeNone) {
         // Just finished a GC, attempt to run finalizers.
         RunFinalization(env);
         CHECK(!env->ExceptionCheck());
       }
       // If we still are over the watermark, attempt a GC for alloc and run finalizers.
-      if (new_native_bytes_allocated > native_footprint_limit_) {
+      if (new_native_bytes_allocated > growth_limit_) {
         CollectGarbageInternal(gc_type, kGcCauseForNativeAlloc, false);
         RunFinalization(env);
         native_need_to_run_finalization_ = false;
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index d2cc350..a0fcf53 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -887,9 +887,6 @@
   // The watermark at which a concurrent GC is requested by registerNativeAllocation.
   size_t native_footprint_gc_watermark_;
 
-  // The watermark at which a GC is performed inside of registerNativeAllocation.
-  size_t native_footprint_limit_;
-
   // Whether or not we need to run finalizers in the next native allocation.
   bool native_need_to_run_finalization_;
 
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index b9379f5..330d235 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -921,7 +921,7 @@
   ObjectId stringObject = request.ReadObjectId();
   std::string str(Dbg::StringToUtf8(stringObject));
 
-  VLOG(jdwp) << StringPrintf("    --> %s", PrintableString(str).c_str());
+  VLOG(jdwp) << StringPrintf("    --> %s", PrintableString(str.c_str()).c_str());
 
   expandBufAddUtf8String(pReply, str);
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a68c658..c2912be 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -638,7 +638,7 @@
   CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize);
 
   std::unique_ptr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized));
-  if (options.get() == NULL) {
+  if (options.get() == nullptr) {
     LOG(ERROR) << "Failed to parse options";
     return false;
   }
@@ -761,9 +761,9 @@
   // ClassLinker needs an attached thread, but we can't fully attach a thread without creating
   // objects. We can't supply a thread group yet; it will be fixed later. Since we are the main
   // thread, we do not get a java peer.
-  Thread* self = Thread::Attach("main", false, NULL, false);
+  Thread* self = Thread::Attach("main", false, nullptr, false);
   CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId);
-  CHECK(self != NULL);
+  CHECK(self != nullptr);
 
   // Set us to runnable so tools using a runtime can allocate and GC by default
   self->TransitionFromSuspendedToRunnable();
@@ -793,11 +793,11 @@
       }
     }
   } else {
-    CHECK(options->boot_class_path_ != NULL);
+    CHECK(options->boot_class_path_ != nullptr);
     CHECK_NE(options->boot_class_path_->size(), 0U);
     class_linker_->InitWithoutImage(*options->boot_class_path_);
   }
-  CHECK(class_linker_ != NULL);
+  CHECK(class_linker_ != nullptr);
   verifier::MethodVerifier::Init();
 
   method_trace_ = options->method_trace_;
@@ -823,6 +823,13 @@
   pre_allocated_OutOfMemoryError_ = GcRoot<mirror::Throwable>(self->GetException(NULL));
   self->ClearException();
 
+  // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class
+  // ahead of checking the application's class loader.
+  self->ThrowNewException(ThrowLocation(), "Ljava/lang/NoClassDefFoundError;",
+                          "Class not found using the boot class loader; no stack available");
+  pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(self->GetException(NULL));
+  self->ClearException();
+
   // Look for a native bridge.
   native_bridge_library_filename_ = options->native_bridge_library_filename_;
   android::SetupNativeBridge(native_bridge_library_filename_.c_str(), &native_bridge_art_callbacks_);
@@ -1037,12 +1044,20 @@
 
 mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryError() {
   mirror::Throwable* oome = pre_allocated_OutOfMemoryError_.Read();
-  if (oome == NULL) {
+  if (oome == nullptr) {
     LOG(ERROR) << "Failed to return pre-allocated OOME";
   }
   return oome;
 }
 
+mirror::Throwable* Runtime::GetPreAllocatedNoClassDefFoundError() {
+  mirror::Throwable* ncdfe = pre_allocated_NoClassDefFoundError_.Read();
+  if (ncdfe == nullptr) {
+    LOG(ERROR) << "Failed to return pre-allocated NoClassDefFoundError";
+  }
+  return ncdfe;
+}
+
 void Runtime::VisitConstantRoots(RootCallback* callback, void* arg) {
   // Visit the classes held as static in mirror classes, these can be visited concurrently and only
   // need to be visited once per GC since they never change.
@@ -1081,6 +1096,10 @@
   }
   resolution_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
   DCHECK(!resolution_method_.IsNull());
+  if (!pre_allocated_NoClassDefFoundError_.IsNull()) {
+    pre_allocated_NoClassDefFoundError_.VisitRoot(callback, arg, 0, kRootVMInternal);
+    DCHECK(!pre_allocated_NoClassDefFoundError_.IsNull());
+  }
   if (HasImtConflictMethod()) {
     imt_conflict_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
   }
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 183bf52..db7b476 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -246,6 +246,9 @@
 
   mirror::Throwable* GetPreAllocatedOutOfMemoryError() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  mirror::Throwable* GetPreAllocatedNoClassDefFoundError()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   const std::vector<std::string>& GetProperties() const {
     return properties_;
   }
@@ -501,6 +504,7 @@
 
   GcRoot<mirror::ArtMethod> callee_save_methods_[kLastCalleeSaveType];
   GcRoot<mirror::Throwable> pre_allocated_OutOfMemoryError_;
+  GcRoot<mirror::Throwable> pre_allocated_NoClassDefFoundError_;
   GcRoot<mirror::ArtMethod> resolution_method_;
   GcRoot<mirror::ArtMethod> imt_conflict_method_;
   GcRoot<mirror::ObjectArray<mirror::ArtMethod>> default_imt_;
diff --git a/runtime/utf.cc b/runtime/utf.cc
index e48d6d2..02cbe3b 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -85,6 +85,14 @@
   return hash;
 }
 
+int32_t ComputeUtf8Hash(const char* chars) {
+  int32_t hash = 0;
+  while (*chars != '\0') {
+    hash = hash * 31 + GetUtf16FromUtf8(&chars);
+  }
+  return hash;
+}
+
 int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8_1, const uint16_t* utf8_2) {
   for (;;) {
     if (*utf8_1 == '\0') {
diff --git a/runtime/utf.h b/runtime/utf.h
index 63cdbdc..a09db9d 100644
--- a/runtime/utf.h
+++ b/runtime/utf.h
@@ -79,6 +79,9 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count);
 
+// Compute a hash code of a UTF-8 string.
+int32_t ComputeUtf8Hash(const char* chars);
+
 /*
  * Retrieve the next UTF-16 character from a UTF-8 string.
  *
diff --git a/runtime/utils.cc b/runtime/utils.cc
index d704eec..c4e84f3 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -568,10 +568,10 @@
   return result;
 }
 
-std::string PrintableString(const std::string& utf) {
+std::string PrintableString(const char* utf) {
   std::string result;
   result += '"';
-  const char* p = utf.c_str();
+  const char* p = utf;
   size_t char_count = CountModifiedUtf8Chars(p);
   for (size_t i = 0; i < char_count; ++i) {
     uint16_t ch = GetUtf16FromUtf8(&p);
diff --git a/runtime/utils.h b/runtime/utils.h
index c4a5895..a34a01d 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -261,7 +261,7 @@
 
 // Returns an ASCII string corresponding to the given UTF-8 string.
 // Java escapes are used for non-ASCII characters.
-std::string PrintableString(const std::string& utf8);
+std::string PrintableString(const char* utf8);
 
 // Tests whether 's' starts with 'prefix'.
 bool StartsWith(const std::string& s, const char* prefix);
diff --git a/test/004-NativeAllocations/src/Main.java b/test/004-NativeAllocations/src/Main.java
index 483c667..a99fe92 100644
--- a/test/004-NativeAllocations/src/Main.java
+++ b/test/004-NativeAllocations/src/Main.java
@@ -15,6 +15,7 @@
  */
 
 import java.lang.reflect.*;
+import java.lang.Runtime;
 
 public class Main {
     static Object nativeLock = new Object();
@@ -22,7 +23,7 @@
     static Object runtime;
     static Method register_native_allocation;
     static Method register_native_free;
-    static int maxMem = 64 * 1024 * 1024;
+    static long maxMem = 0;
 
     static class NativeAllocation {
         private int bytes;
@@ -52,8 +53,9 @@
         runtime = get_runtime.invoke(null);
         register_native_allocation = vm_runtime.getDeclaredMethod("registerNativeAllocation", Integer.TYPE);
         register_native_free = vm_runtime.getDeclaredMethod("registerNativeFree", Integer.TYPE);
+        maxMem = Runtime.getRuntime().maxMemory();
         int count = 16;
-        int size = 512 * 0x400;
+        int size = (int)(maxMem / 2 / count);
         int allocation_count = 256;
         NativeAllocation[] allocations = new NativeAllocation[count];
         for (int i = 0; i < allocation_count; ++i) {