merge in master-release history after reset to master
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 591d92a..6800f7b 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -211,7 +211,7 @@
                               class_loader, dex_file);
 
   cu.NewTimingSplit("MIROpt:CheckFilters");
-  if (cu.mir_graph->SkipCompilation(Runtime::Current()->GetCompilerFilter())) {
+  if (cu.mir_graph->SkipCompilation()) {
     return NULL;
   }
 
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index ab55333..7ce8f69 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -18,6 +18,7 @@
 #include "dataflow_iterator-inl.h"
 #include "dex/quick/dex_file_method_inliner.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "driver/compiler_options.h"
 
 namespace art {
 
@@ -958,7 +959,7 @@
   }
 
   // Complex, logic-intensive?
-  if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) &&
+  if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) &&
       stats->branch_ratio > 0.3) {
     return false;
   }
@@ -984,7 +985,7 @@
   }
 
   // If significant in size and high proportion of expensive operations, skip.
-  if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) &&
+  if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) &&
       (stats->heavyweight_ratio > 0.3)) {
     return true;
   }
@@ -996,12 +997,14 @@
   * Will eventually want this to be a bit more sophisticated and happen at verification time.
   * Ultimate goal is to drive with profile data.
   */
-bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) {
-  if (compiler_filter == Runtime::kEverything) {
+bool MIRGraph::SkipCompilation() {
+  const CompilerOptions& compiler_options = cu_->compiler_driver->GetCompilerOptions();
+  CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter();
+  if (compiler_filter == CompilerOptions::kEverything) {
     return false;
   }
 
-  if (compiler_filter == Runtime::kInterpretOnly) {
+  if (compiler_filter == CompilerOptions::kInterpretOnly) {
     LOG(WARNING) << "InterpretOnly should ideally be filtered out prior to parsing.";
     return true;
   }
@@ -1010,17 +1013,17 @@
   size_t small_cutoff = 0;
   size_t default_cutoff = 0;
   switch (compiler_filter) {
-    case Runtime::kBalanced:
-      small_cutoff = Runtime::Current()->GetSmallMethodThreshold();
-      default_cutoff = Runtime::Current()->GetLargeMethodThreshold();
+    case CompilerOptions::kBalanced:
+      small_cutoff = compiler_options.GetSmallMethodThreshold();
+      default_cutoff = compiler_options.GetLargeMethodThreshold();
       break;
-    case Runtime::kSpace:
-      small_cutoff = Runtime::Current()->GetTinyMethodThreshold();
-      default_cutoff = Runtime::Current()->GetSmallMethodThreshold();
+    case CompilerOptions::kSpace:
+      small_cutoff = compiler_options.GetTinyMethodThreshold();
+      default_cutoff = compiler_options.GetSmallMethodThreshold();
       break;
-    case Runtime::kSpeed:
-      small_cutoff = Runtime::Current()->GetHugeMethodThreshold();
-      default_cutoff = Runtime::Current()->GetHugeMethodThreshold();
+    case CompilerOptions::kSpeed:
+      small_cutoff = compiler_options.GetHugeMethodThreshold();
+      default_cutoff = compiler_options.GetHugeMethodThreshold();
       break;
     default:
       LOG(FATAL) << "Unexpected compiler_filter_: " << compiler_filter;
@@ -1033,17 +1036,17 @@
    * Filter 1: Huge methods are likely to be machine generated, but some aren't.
    * If huge, assume we won't compile, but allow futher analysis to turn it back on.
    */
-  if (GetNumDalvikInsns() > Runtime::Current()->GetHugeMethodThreshold()) {
+  if (compiler_options.IsHugeMethod(GetNumDalvikInsns())) {
     skip_compilation = true;
     // If we're got a huge number of basic blocks, don't bother with further analysis.
-    if (static_cast<size_t>(num_blocks_) > (Runtime::Current()->GetHugeMethodThreshold() / 2)) {
+    if (static_cast<size_t>(num_blocks_) > (compiler_options.GetHugeMethodThreshold() / 2)) {
       return true;
     }
-  } else if (GetNumDalvikInsns() > Runtime::Current()->GetLargeMethodThreshold() &&
+  } else if (compiler_options.IsLargeMethod(GetNumDalvikInsns()) &&
     /* If it's large and contains no branches, it's likely to be machine generated initialization */
       (GetBranchCount() == 0)) {
     return true;
-  } else if (compiler_filter == Runtime::kSpeed) {
+  } else if (compiler_filter == CompilerOptions::kSpeed) {
     // If not huge, compile.
     return false;
   }
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index d844aac..2174f67 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -372,7 +372,7 @@
    * Examine the graph to determine whether it's worthwile to spend the time compiling
    * this method.
    */
-  bool SkipCompilation(Runtime::CompilerFilter compiler_filter);
+  bool SkipCompilation();
 
   /*
    * Parse dex method and add MIR at current insert point.  Returns id (which is
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index edccec5..947c22d 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -19,6 +19,8 @@
 #include "base/stl_util.h"
 #include "base/mutex.h"
 #include "base/mutex-inl.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "thread.h"
 #include "thread-inl.h"
 #include "verified_method.h"
@@ -27,8 +29,9 @@
 
 namespace art {
 
-VerificationResults::VerificationResults()
-    : verified_methods_lock_("compiler verified methods lock"),
+VerificationResults::VerificationResults(const CompilerOptions* compiler_options)
+    : compiler_options_(compiler_options),
+      verified_methods_lock_("compiler verified methods lock"),
       verified_methods_(),
       rejected_classes_lock_("compiler rejected classes lock"),
       rejected_classes_() {
@@ -43,6 +46,7 @@
 }
 
 bool VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
+  DCHECK(method_verifier != NULL);
   MethodReference ref = method_verifier->GetMethodReference();
   bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
   // TODO: Check also for virtual/interface invokes when DEX-to-DEX supports devirtualization.
@@ -95,16 +99,18 @@
 bool VerificationResults::IsCandidateForCompilation(MethodReference& method_ref,
                                                     const uint32_t access_flags) {
 #ifdef ART_SEA_IR_MODE
-    bool use_sea = Runtime::Current()->IsSeaIRMode();
-    use_sea = use_sea && (std::string::npos != PrettyMethod(
-                          method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
-    if (use_sea) return true;
+  bool use_sea = compiler_options_->GetSeaIrMode();
+  use_sea = use_sea && (std::string::npos != PrettyMethod(
+                        method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
+  if (use_sea) {
+    return true;
+  }
 #endif
   // Don't compile class initializers, ever.
   if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
     return false;
   }
-  return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly);
+  return (compiler_options_->GetCompilerFilter() != CompilerOptions::kInterpretOnly);
 }
 
 }  // namespace art
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 2eb0713..278182f 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -33,11 +33,13 @@
 class MethodVerifier;
 }  // namespace verifier
 
+class CompilerOptions;
 class VerifiedMethod;
 
+// Used by CompilerCallbacks to track verification information from the Runtime.
 class VerificationResults {
   public:
-    VerificationResults();
+    explicit VerificationResults(const CompilerOptions* compiler_options);
     ~VerificationResults();
 
     bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
@@ -50,15 +52,17 @@
     void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
     bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
 
-    static bool IsCandidateForCompilation(MethodReference& method_ref,
-                                          const uint32_t access_flags);
+    bool IsCandidateForCompilation(MethodReference& method_ref,
+                                   const uint32_t access_flags);
 
   private:
+    const CompilerOptions* compiler_options_;
+
     // Verified methods.
     typedef SafeMap<MethodReference, const VerifiedMethod*,
         MethodReferenceComparator> VerifiedMethodMap;
     ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-    VerifiedMethodMap verified_methods_;
+    VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_);
 
     // Rejected classes.
     ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/compiler/driver/compiler_callbacks_impl.h b/compiler/driver/compiler_callbacks_impl.h
new file mode 100644
index 0000000..fd2cd4a
--- /dev/null
+++ b/compiler/driver/compiler_callbacks_impl.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
+#define ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
+
+#include "compiler_callbacks.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "verifier/method_verifier-inl.h"
+
+namespace art {
+
+class CompilerCallbacksImpl : public CompilerCallbacks {
+  public:
+    CompilerCallbacksImpl(VerificationResults* verification_results,
+                          DexFileToMethodInlinerMap* method_inliner_map)
+        : verification_results_(verification_results),
+          method_inliner_map_(method_inliner_map) {
+      CHECK(verification_results != nullptr);
+      CHECK(method_inliner_map != nullptr);
+    }
+
+    virtual ~CompilerCallbacksImpl() { }
+
+    virtual bool MethodVerified(verifier::MethodVerifier* verifier)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+      bool result = verification_results_->ProcessVerifiedMethod(verifier);
+      if (result) {
+        MethodReference ref = verifier->GetMethodReference();
+        method_inliner_map_->GetMethodInliner(ref.dex_file)
+            ->AnalyseMethodCode(verifier);
+      }
+      return result;
+    }
+    virtual void ClassRejected(ClassReference ref) {
+      verification_results_->AddRejectedClass(ref);
+    }
+
+  private:
+    VerificationResults* const verification_results_;
+    DexFileToMethodInlinerMap* const method_inliner_map_;
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5adb792..1b284de 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -293,14 +293,16 @@
                                               jobject class_loader,
                                               const art::DexFile& dex_file);
 
-CompilerDriver::CompilerDriver(VerificationResults* verification_results,
+CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
+                               VerificationResults* verification_results,
                                DexFileToMethodInlinerMap* method_inliner_map,
                                CompilerBackend::Kind compiler_backend_kind,
                                InstructionSet instruction_set,
                                InstructionSetFeatures instruction_set_features,
                                bool image, DescriptorSet* image_classes, size_t thread_count,
                                bool dump_stats, bool dump_passes, CumulativeLogger* timer)
-    : verification_results_(verification_results),
+    : compiler_options_(compiler_options),
+      verification_results_(verification_results),
       method_inliner_map_(method_inliner_map),
       compiler_backend_(CompilerBackend::Create(compiler_backend_kind)),
       instruction_set_(instruction_set),
@@ -325,6 +327,9 @@
       dedupe_mapping_table_("dedupe mapping table"),
       dedupe_vmap_table_("dedupe vmap table"),
       dedupe_gc_map_("dedupe gc map") {
+  DCHECK(compiler_options_ != nullptr);
+  DCHECK(verification_results_ != nullptr);
+  DCHECK(method_inliner_map_ != nullptr);
 
   CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, NULL), "compiler tls key");
 
@@ -447,13 +452,12 @@
 }
 
 static DexToDexCompilationLevel GetDexToDexCompilationlevel(
-    SirtRef<mirror::ClassLoader>& class_loader, const DexFile& dex_file,
+    Thread* self, SirtRef<mirror::ClassLoader>& class_loader, const DexFile& dex_file,
     const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   const char* descriptor = dex_file.GetClassDescriptor(class_def);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  mirror::Class* klass = class_linker->FindClass(descriptor, class_loader);
+  mirror::Class* klass = class_linker->FindClass(self, descriptor, class_loader);
   if (klass == NULL) {
-    Thread* self = Thread::Current();
     CHECK(self->IsExceptionPending());
     self->ClearException();
     return kDontDexToDexCompile;
@@ -515,7 +519,8 @@
     const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
     SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
                                               soa.Decode<mirror::ClassLoader*>(jclass_loader));
-    dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, *dex_file, class_def);
+    dex_to_dex_compilation_level = GetDexToDexCompilationlevel(self, class_loader, *dex_file,
+                                                               class_def);
   }
   CompileMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx, jclass_loader,
                 *dex_file, dex_to_dex_compilation_level);
@@ -633,7 +638,7 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   for (auto it = image_classes_->begin(), end = image_classes_->end(); it != end;) {
     const std::string& descriptor(*it);
-    SirtRef<mirror::Class> klass(self, class_linker->FindSystemClass(descriptor.c_str()));
+    SirtRef<mirror::Class> klass(self, class_linker->FindSystemClass(self, descriptor.c_str()));
     if (klass.get() == NULL) {
       VLOG(compiler) << "Failed to find class " << descriptor;
       image_classes_->erase(it++);
@@ -648,7 +653,7 @@
   // Do this here so that exception classes appear to have been specified image classes.
   std::set<std::pair<uint16_t, const DexFile*> > unresolved_exception_types;
   SirtRef<mirror::Class> java_lang_Throwable(self,
-                                     class_linker->FindSystemClass("Ljava/lang/Throwable;"));
+                                     class_linker->FindSystemClass(self, "Ljava/lang/Throwable;"));
   do {
     unresolved_exception_types.clear();
     class_linker->VisitClasses(ResolveCatchBlockExceptionsClassVisitor,
@@ -1661,7 +1666,8 @@
   jobject jclass_loader = manager->GetClassLoader();
   SirtRef<mirror::ClassLoader> class_loader(
       soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
-  SirtRef<mirror::Class> klass(soa.Self(), class_linker->FindClass(descriptor, class_loader));
+  SirtRef<mirror::Class> klass(soa.Self(), class_linker->FindClass(soa.Self(), descriptor,
+                                                                   class_loader));
   if (klass.get() == nullptr) {
     CHECK(soa.Self()->IsExceptionPending());
     soa.Self()->ClearException();
@@ -1716,7 +1722,8 @@
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
                                             soa.Decode<mirror::ClassLoader*>(jclass_loader));
   SirtRef<mirror::Class> klass(soa.Self(),
-                               manager->GetClassLinker()->FindClass(descriptor, class_loader));
+                               manager->GetClassLinker()->FindClass(soa.Self(), descriptor,
+                                                                    class_loader));
 
   if (klass.get() != nullptr && !SkipClass(jclass_loader, dex_file, klass.get())) {
     // Only try to initialize classes that were successfully verified.
@@ -1861,7 +1868,8 @@
     ScopedObjectAccess soa(Thread::Current());
     SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
                                               soa.Decode<mirror::ClassLoader*>(jclass_loader));
-    dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, dex_file, class_def);
+    dex_to_dex_compilation_level = GetDexToDexCompilationlevel(soa.Self(), class_loader, dex_file,
+                                                               class_def);
   }
   ClassDataItemIterator it(dex_file, class_data);
   // Skip fields
@@ -1929,7 +1937,7 @@
   } else if ((access_flags & kAccAbstract) != 0) {
   } else {
     MethodReference method_ref(&dex_file, method_idx);
-    bool compile = VerificationResults::IsCandidateForCompilation(method_ref, access_flags);
+    bool compile = verification_results_->IsCandidateForCompilation(method_ref, access_flags);
 
     if (compile) {
       // NOTE: if compiler declines to compile this method, it will return NULL.
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 092fe52..377eb6f 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -45,6 +45,7 @@
 }  // namespace verifier
 
 class AOTCompilationStats;
+class CompilerOptions;
 class DexCompilationUnit;
 class DexFileToMethodInlinerMap;
 class InlineIGetIPutData;
@@ -94,7 +95,8 @@
   // enabled.  "image_classes" lets the compiler know what classes it
   // can assume will be in the image, with NULL implying all available
   // classes.
-  explicit CompilerDriver(VerificationResults* verification_results,
+  explicit CompilerDriver(const CompilerOptions* compiler_options,
+                          VerificationResults* verification_results,
                           DexFileToMethodInlinerMap* method_inliner_map,
                           CompilerBackend::Kind compiler_backend_kind,
                           InstructionSet instruction_set,
@@ -129,6 +131,11 @@
     return instruction_set_features_;
   }
 
+  const CompilerOptions& GetCompilerOptions() const {
+    DCHECK(compiler_options_ != nullptr);
+    return *compiler_options_;
+  }
+
   CompilerBackend* GetCompilerBackend() const {
     return compiler_backend_.get();
   }
@@ -551,8 +558,9 @@
   std::vector<const CallPatchInformation*> methods_to_patch_;
   std::vector<const TypePatchInformation*> classes_to_patch_;
 
-  VerificationResults* verification_results_;
-  DexFileToMethodInlinerMap* method_inliner_map_;
+  const CompilerOptions* const compiler_options_;
+  VerificationResults* const verification_results_;
+  DexFileToMethodInlinerMap* const method_inliner_map_;
 
   UniquePtr<CompilerBackend> compiler_backend_;
 
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 0d0c204..ec0a8bd 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -79,9 +79,8 @@
       const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
       const char* descriptor = dex_file.GetClassDescriptor(class_def);
       ScopedObjectAccess soa(Thread::Current());
-      Thread* self = Thread::Current();
-      SirtRef<mirror::ClassLoader> loader(self, soa.Decode<mirror::ClassLoader*>(class_loader));
-      mirror::Class* c = class_linker->FindClass(descriptor, loader);
+      SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(class_loader));
+      mirror::Class* c = class_linker->FindClass(soa.Self(), descriptor, loader);
       CHECK(c != NULL);
       for (size_t i = 0; i < c->NumDirectMethods(); i++) {
         MakeExecutable(c->GetDirectMethod(i));
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
new file mode 100644
index 0000000..9f6745b
--- /dev/null
+++ b/compiler/driver/compiler_options.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
+
+namespace art {
+
+class CompilerOptions {
+ public:
+  enum CompilerFilter {
+    kInterpretOnly,       // Compile nothing.
+    kSpace,               // Maximize space savings.
+    kBalanced,            // Try to get the best performance return on compilation investment.
+    kSpeed,               // Maximize runtime performance.
+    kEverything           // Force compilation (Note: excludes compilaton of class initializers).
+  };
+
+  // Guide heuristics to determine whether to compile method if profile data not available.
+  static const CompilerFilter kDefaultCompilerFilter = kSpeed;
+  static const size_t kDefaultHugeMethodThreshold = 10000;
+  static const size_t kDefaultLargeMethodThreshold = 600;
+  static const size_t kDefaultSmallMethodThreshold = 60;
+  static const size_t kDefaultTinyMethodThreshold = 20;
+  static const size_t kDefaultNumDexMethodsThreshold = 900;
+
+  CompilerOptions() :
+    compiler_filter_(kDefaultCompilerFilter),
+    huge_method_threshold_(kDefaultHugeMethodThreshold),
+    large_method_threshold_(kDefaultLargeMethodThreshold),
+    small_method_threshold_(kDefaultSmallMethodThreshold),
+    tiny_method_threshold_(kDefaultTinyMethodThreshold),
+    num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold)
+#ifdef ART_SEA_IR_MODE
+    , sea_ir_mode_(false)
+#endif
+    {}
+
+  CompilerOptions(CompilerFilter compiler_filter,
+                  size_t huge_method_threshold,
+                  size_t large_method_threshold,
+                  size_t small_method_threshold,
+                  size_t tiny_method_threshold,
+                  size_t num_dex_methods_threshold
+#ifdef ART_SEA_IR_MODE
+                  , bool sea_ir_mode
+#endif
+                  ) :  // NOLINT(whitespace/parens)
+    compiler_filter_(compiler_filter),
+    huge_method_threshold_(huge_method_threshold),
+    large_method_threshold_(large_method_threshold),
+    small_method_threshold_(small_method_threshold),
+    tiny_method_threshold_(tiny_method_threshold),
+    num_dex_methods_threshold_(num_dex_methods_threshold)
+#ifdef ART_SEA_IR_MODE
+    , sea_ir_mode_(sea_ir_mode)
+#endif
+    {}
+
+  CompilerFilter GetCompilerFilter() const {
+    return compiler_filter_;
+  }
+
+  void SetCompilerFilter(CompilerFilter compiler_filter) {
+    compiler_filter_ = compiler_filter;
+  }
+
+  size_t GetHugeMethodThreshold() const {
+    return huge_method_threshold_;
+  }
+
+  size_t GetLargeMethodThreshold() const {
+    return large_method_threshold_;
+  }
+
+  size_t GetSmallMethodThreshold() const {
+    return small_method_threshold_;
+  }
+
+  size_t GetTinyMethodThreshold() const {
+    return tiny_method_threshold_;
+  }
+
+  bool IsHugeMethod(size_t num_dalvik_instructions) const {
+    return num_dalvik_instructions > huge_method_threshold_;
+  }
+
+  bool IsLargeMethod(size_t num_dalvik_instructions) const {
+    return num_dalvik_instructions > large_method_threshold_;
+  }
+
+  bool IsSmallMethod(size_t num_dalvik_instructions) const {
+    return num_dalvik_instructions > small_method_threshold_;
+  }
+
+  bool IsTinyMethod(size_t num_dalvik_instructions) const {
+    return num_dalvik_instructions > tiny_method_threshold_;
+  }
+
+  size_t GetNumDexMethodsThreshold() const {
+    return num_dex_methods_threshold_;
+  }
+
+#ifdef ART_SEA_IR_MODE
+  bool GetSeaIrMode();
+#endif
+
+ private:
+  CompilerFilter compiler_filter_;
+  size_t huge_method_threshold_;
+  size_t large_method_threshold_;
+  size_t small_method_threshold_;
+  size_t tiny_method_threshold_;
+  size_t num_dex_methods_threshold_;
+
+#ifdef ART_SEA_IR_MODE
+  bool sea_ir_mode_;
+#endif
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 3406fe6..49cabdc 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -151,7 +151,7 @@
   for (size_t i = 0; i < dex->NumClassDefs(); ++i) {
     const DexFile::ClassDef& class_def = dex->GetClassDef(i);
     const char* descriptor = dex->GetClassDescriptor(class_def);
-    mirror::Class* klass = class_linker_->FindSystemClass(descriptor);
+    mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor);
     EXPECT_TRUE(klass != nullptr) << descriptor;
     if (image_classes.find(descriptor) != image_classes.end()) {
       // Image classes should be located inside the image.
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 58f66b9..c8447be 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -401,7 +401,8 @@
   Runtime* runtime = Runtime::Current();
   ClassLinker* class_linker = runtime->GetClassLinker();
   Thread* self = Thread::Current();
-  SirtRef<Class> object_array_class(self, class_linker->FindSystemClass("[Ljava/lang/Object;"));
+  SirtRef<Class> object_array_class(self, class_linker->FindSystemClass(self,
+                                                                        "[Ljava/lang/Object;"));
 
   // build an Object[] of all the DexCaches used in the source_space_
   ObjectArray<Object>* dex_caches = ObjectArray<Object>::Alloc(self, object_array_class.get(),
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index c77d319..1bdff37 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -50,7 +50,7 @@
     ScopedObjectAccess soa(Thread::Current());
     SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(class_loader));
     // Compile the native method before starting the runtime
-    mirror::Class* c = class_linker_->FindClass("LMyClassNatives;", loader);
+    mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader);
     mirror::ArtMethod* method;
     if (direct) {
       method = c->FindDirectMethod(method_name, method_sig);
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 10d2c5c..e91ffcb 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -91,11 +91,14 @@
   InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
 
   InstructionSetFeatures insn_features;
-  verification_results_.reset(new VerificationResults);
+  compiler_options_.reset(new CompilerOptions);
+  verification_results_.reset(new VerificationResults(compiler_options_.get()));
   method_inliner_map_.reset(new DexFileToMethodInlinerMap);
-  callbacks_.Reset(verification_results_.get(), method_inliner_map_.get());
+  callbacks_.reset(new CompilerCallbacksImpl(verification_results_.get(),
+                                             method_inliner_map_.get()));
   timer_.reset(new CumulativeLogger("Compilation times"));
-  compiler_driver_.reset(new CompilerDriver(verification_results_.get(),
+  compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+                                            verification_results_.get(),
                                             method_inliner_map_.get(),
                                             compiler_backend, insn_set,
                                             insn_features, false, NULL, 2, true, true,
@@ -150,8 +153,8 @@
       num_virtual_methods = it.NumVirtualMethods();
     }
     const char* descriptor = dex_file->GetClassDescriptor(class_def);
-    SirtRef<mirror::ClassLoader> loader(Thread::Current(), nullptr);
-    mirror::Class* klass = class_linker->FindClass(descriptor, loader);
+    SirtRef<mirror::ClassLoader> loader(soa.Self(), nullptr);
+    mirror::Class* klass = class_linker->FindClass(soa.Self(), descriptor, loader);
 
     UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(i));
     CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class->GetStatus()) << descriptor;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index bfda17d..a082e36 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -34,7 +34,9 @@
 #include "compiler_callbacks.h"
 #include "dex_file-inl.h"
 #include "dex/verification_results.h"
+#include "driver/compiler_callbacks_impl.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "elf_fixup.h"
 #include "elf_stripper.h"
 #include "gc/space/image_space.h"
@@ -54,15 +56,22 @@
 #include "scoped_thread_state_change.h"
 #include "sirt_ref.h"
 #include "vector_output_stream.h"
-#include "verifier/method_verifier.h"
-#include "verifier/method_verifier-inl.h"
 #include "well_known_classes.h"
 #include "zip_archive.h"
 
-#include "dex/quick/dex_file_to_method_inliner_map.h"
-
 namespace art {
 
+static int original_argc;
+static char** original_argv;
+
+static std::string CommandLine() {
+  std::vector<std::string> command;
+  for (int i = 0; i < original_argc; ++i) {
+    command.push_back(original_argv[i]);
+  }
+  return Join(command, ' ');
+}
+
 static void UsageErrorV(const char* fmt, va_list ap) {
   std::string error;
   StringAppendV(&error, fmt, ap);
@@ -82,6 +91,8 @@
   UsageErrorV(fmt, ap);
   va_end(ap);
 
+  UsageError("Command: %s", CommandLine().c_str());
+
   UsageError("Usage: dex2oat [options]...");
   UsageError("");
   UsageError("  --dex-file=<dex-file>: specifies a .dex file to compile.");
@@ -147,6 +158,46 @@
   UsageError("      Example: --compiler-backend=Portable");
   UsageError("      Default: Quick");
   UsageError("");
+  UsageError("  --compiler-filter=(interpret-only|space|balanced|speed|everything): select");
+  UsageError("      compiler filter.");
+  UsageError("      Example: --compiler-filter=everything");
+#if ART_SMALL_MODE
+  UsageError("      Default: interpret-only");
+#else
+  UsageError("      Default: speed");
+#endif
+  UsageError("");
+  UsageError("  --huge-method-max=<method-instruction-count>: the threshold size for a huge");
+  UsageError("      method for compiler filter tuning.");
+  UsageError("      Example: --huge-method-max=%d", CompilerOptions::kDefaultHugeMethodThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultHugeMethodThreshold);
+  UsageError("");
+  UsageError("  --huge-method-max=<method-instruction-count>: threshold size for a huge");
+  UsageError("      method for compiler filter tuning.");
+  UsageError("      Example: --huge-method-max=%d", CompilerOptions::kDefaultHugeMethodThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultHugeMethodThreshold);
+  UsageError("");
+  UsageError("  --large-method-max=<method-instruction-count>: threshold size for a large");
+  UsageError("      method for compiler filter tuning.");
+  UsageError("      Example: --large-method-max=%d", CompilerOptions::kDefaultLargeMethodThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultLargeMethodThreshold);
+  UsageError("");
+  UsageError("  --small-method-max=<method-instruction-count>: threshold size for a small");
+  UsageError("      method for compiler filter tuning.");
+  UsageError("      Example: --small-method-max=%d", CompilerOptions::kDefaultSmallMethodThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultSmallMethodThreshold);
+  UsageError("");
+  UsageError("  --tiny-method-max=<method-instruction-count>: threshold size for a tiny");
+  UsageError("      method for compiler filter tuning.");
+  UsageError("      Example: --tiny-method-max=%d", CompilerOptions::kDefaultTinyMethodThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultTinyMethodThreshold);
+  UsageError("");
+  UsageError("  --num-dex-methods=<method-count>: threshold size for a small dex file for");
+  UsageError("      compiler filter tuning. If the input has fewer than this many methods");
+  UsageError("      and the filter is not interpret-only, overrides the filter to use speed");
+  UsageError("      Example: --num-dex-method=%d", CompilerOptions::kDefaultNumDexMethodsThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultNumDexMethodsThreshold);
+  UsageError("");
   UsageError("  --host: used with Portable backend to link against host runtime libraries");
   UsageError("");
   UsageError("  --dump-timing: display a breakdown of where time was spent");
@@ -163,15 +214,25 @@
 class Dex2Oat {
  public:
   static bool Create(Dex2Oat** p_dex2oat,
-                     Runtime::Options& options,
+                     const Runtime::Options& runtime_options,
+                     const CompilerOptions& compiler_options,
                      CompilerBackend::Kind compiler_backend,
                      InstructionSet instruction_set,
                      InstructionSetFeatures instruction_set_features,
+                     VerificationResults* verification_results,
+                     DexFileToMethodInlinerMap* method_inliner_map,
                      size_t thread_count)
       SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
-    UniquePtr<Dex2Oat> dex2oat(new Dex2Oat(compiler_backend, instruction_set,
-                                           instruction_set_features, thread_count));
-    if (!dex2oat->CreateRuntime(options, instruction_set)) {
+    CHECK(verification_results != nullptr);
+    CHECK(method_inliner_map != nullptr);
+    UniquePtr<Dex2Oat> dex2oat(new Dex2Oat(&compiler_options,
+                                           compiler_backend,
+                                           instruction_set,
+                                           instruction_set_features,
+                                           verification_results,
+                                           method_inliner_map,
+                                           thread_count));
+    if (!dex2oat->CreateRuntime(runtime_options, instruction_set)) {
       *p_dex2oat = NULL;
       return false;
     }
@@ -275,8 +336,9 @@
       Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
     }
 
-    UniquePtr<CompilerDriver> driver(new CompilerDriver(verification_results_.get(),
-                                                        method_inliner_map_.get(),
+    UniquePtr<CompilerDriver> driver(new CompilerDriver(compiler_options_,
+                                                        verification_results_,
+                                                        method_inliner_map_,
                                                         compiler_backend_,
                                                         instruction_set_,
                                                         instruction_set_features_,
@@ -353,53 +415,30 @@
   }
 
  private:
-  class Dex2OatCompilerCallbacks : public CompilerCallbacks {
-    public:
-      Dex2OatCompilerCallbacks(VerificationResults* verification_results,
-                               DexFileToMethodInlinerMap* method_inliner_map)
-          : verification_results_(verification_results),
-            method_inliner_map_(method_inliner_map) { }
-      virtual ~Dex2OatCompilerCallbacks() { }
-
-      virtual bool MethodVerified(verifier::MethodVerifier* verifier)
-          SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-        bool result = verification_results_->ProcessVerifiedMethod(verifier);
-        if (result) {
-          MethodReference ref = verifier->GetMethodReference();
-          method_inliner_map_->GetMethodInliner(ref.dex_file)
-              ->AnalyseMethodCode(verifier);
-        }
-        return result;
-      }
-      virtual void ClassRejected(ClassReference ref) {
-        verification_results_->AddRejectedClass(ref);
-      }
-
-    private:
-      VerificationResults* verification_results_;
-      DexFileToMethodInlinerMap* method_inliner_map_;
-  };
-
-  explicit Dex2Oat(CompilerBackend::Kind compiler_backend,
+  explicit Dex2Oat(const CompilerOptions* compiler_options,
+                   CompilerBackend::Kind compiler_backend,
                    InstructionSet instruction_set,
                    InstructionSetFeatures instruction_set_features,
+                   VerificationResults* verification_results,
+                   DexFileToMethodInlinerMap* method_inliner_map,
                    size_t thread_count)
-      : compiler_backend_(compiler_backend),
+      : compiler_options_(compiler_options),
+        compiler_backend_(compiler_backend),
         instruction_set_(instruction_set),
         instruction_set_features_(instruction_set_features),
-        verification_results_(new VerificationResults),
-        method_inliner_map_(new DexFileToMethodInlinerMap),
-        callbacks_(verification_results_.get(), method_inliner_map_.get()),
+        verification_results_(verification_results),
+        method_inliner_map_(method_inliner_map),
         runtime_(nullptr),
         thread_count_(thread_count),
         start_ns_(NanoTime()) {
+    CHECK(compiler_options != nullptr);
+    CHECK(verification_results != nullptr);
+    CHECK(method_inliner_map != nullptr);
   }
 
-  bool CreateRuntime(Runtime::Options& options, InstructionSet instruction_set)
+  bool CreateRuntime(const Runtime::Options& runtime_options, InstructionSet instruction_set)
       SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
-    options.push_back(
-        std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
-    if (!Runtime::Create(options, false)) {
+    if (!Runtime::Create(runtime_options, false)) {
       LOG(ERROR) << "Failed to create runtime";
       return false;
     }
@@ -448,14 +487,14 @@
     return false;
   }
 
+  const CompilerOptions* const compiler_options_;
   const CompilerBackend::Kind compiler_backend_;
 
   const InstructionSet instruction_set_;
   const InstructionSetFeatures instruction_set_features_;
 
-  UniquePtr<VerificationResults> verification_results_;
-  UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
-  Dex2OatCompilerCallbacks callbacks_;
+  VerificationResults* const verification_results_;
+  DexFileToMethodInlinerMap* const method_inliner_map_;
   Runtime* runtime_;
   size_t thread_count_;
   uint64_t start_ns_;
@@ -656,6 +695,9 @@
 }
 
 static int dex2oat(int argc, char** argv) {
+  original_argc = argc;
+  original_argv = argv;
+
   TimingLogger timings("compiler", false, false);
   CumulativeLogger compiler_phases_timings("compilation times");
 
@@ -690,6 +732,12 @@
   CompilerBackend::Kind compiler_backend = kUsePortableCompiler
       ? CompilerBackend::kPortable
       : CompilerBackend::kQuick;
+  const char* compiler_filter_string = NULL;
+  int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold;
+  int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold;
+  int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
+  int tiny_method_threshold = CompilerOptions::kDefaultTinyMethodThreshold;
+  int num_dex_methods_threshold = CompilerOptions::kDefaultNumDexMethodsThreshold;
 
   // Take the default set of instruction features from the build.
   InstructionSetFeatures instruction_set_features =
@@ -713,7 +761,6 @@
   bool dump_slow_timing = kIsDebugBuild;
   bool watch_dog_enabled = !kIsTargetBuild;
 
-
   for (int i = 0; i < argc; i++) {
     const StringPiece option(argv[i]);
     bool log_options = false;
@@ -729,6 +776,9 @@
       if (!ParseInt(zip_fd_str, &zip_fd)) {
         Usage("Failed to parse --zip-fd argument '%s' as an integer", zip_fd_str);
       }
+      if (zip_fd < 0) {
+        Usage("--zip-fd passed a negative value %d", zip_fd);
+      }
     } else if (option.starts_with("--zip-location=")) {
       zip_location = option.substr(strlen("--zip-location=")).data();
     } else if (option.starts_with("--oat-file=")) {
@@ -740,6 +790,9 @@
       if (!ParseInt(oat_fd_str, &oat_fd)) {
         Usage("Failed to parse --oat-fd argument '%s' as an integer", oat_fd_str);
       }
+      if (oat_fd < 0) {
+        Usage("--oat-fd passed a negative value %d", oat_fd);
+      }
     } else if (option == "--watch-dog") {
       watch_dog_enabled = true;
     } else if (option == "--no-watch-dog") {
@@ -793,6 +846,48 @@
       } else if (backend_str == "Portable") {
         compiler_backend = CompilerBackend::kPortable;
       }
+    } else if (option.starts_with("--compiler-filter=")) {
+      compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
+    } else if (option.starts_with("--huge-method-max=")) {
+      const char* threshold = option.substr(strlen("--huge-method-max=")).data();
+      if (!ParseInt(threshold, &huge_method_threshold)) {
+        Usage("Failed to parse --huge-method-max '%s' as an integer", threshold);
+      }
+      if (huge_method_threshold < 0) {
+        Usage("--huge-method-max passed a negative value %s", huge_method_threshold);
+      }
+    } else if (option.starts_with("--large-method-max=")) {
+      const char* threshold = option.substr(strlen("--large-method-max=")).data();
+      if (!ParseInt(threshold, &large_method_threshold)) {
+        Usage("Failed to parse --large-method-max '%s' as an integer", threshold);
+      }
+      if (large_method_threshold < 0) {
+        Usage("--large-method-max passed a negative value %s", large_method_threshold);
+      }
+    } else if (option.starts_with("--small-method-max=")) {
+      const char* threshold = option.substr(strlen("--small-method-max=")).data();
+      if (!ParseInt(threshold, &small_method_threshold)) {
+        Usage("Failed to parse --small-method-max '%s' as an integer", threshold);
+      }
+      if (small_method_threshold < 0) {
+        Usage("--small-method-max passed a negative value %s", small_method_threshold);
+      }
+    } else if (option.starts_with("--tiny-method-max=")) {
+      const char* threshold = option.substr(strlen("--tiny-method-max=")).data();
+      if (!ParseInt(threshold, &tiny_method_threshold)) {
+        Usage("Failed to parse --tiny-method-max '%s' as an integer", threshold);
+      }
+      if (tiny_method_threshold < 0) {
+        Usage("--tiny-method-max passed a negative value %s", tiny_method_threshold);
+      }
+    } else if (option.starts_with("--num-dex-methods=")) {
+      const char* threshold = option.substr(strlen("--num-dex-methods=")).data();
+      if (!ParseInt(threshold, &num_dex_methods_threshold)) {
+        Usage("Failed to parse --num-dex-methods '%s' as an integer", threshold);
+      }
+      if (num_dex_methods_threshold < 0) {
+        Usage("--num-dex-methods passed a negative value %s", num_dex_methods_threshold);
+      }
     } else if (option == "--host") {
       is_host = true;
     } else if (option == "--runtime-arg") {
@@ -915,6 +1010,44 @@
     oat_unstripped += oat_filename;
   }
 
+  if (compiler_filter_string == NULL) {
+    if (image) {
+      compiler_filter_string = "everything";
+    } else {
+#if ART_SMALL_MODE
+      compiler_filter_string = "interpret-only";
+#else
+      compiler_filter_string = "speed";
+#endif
+    }
+  }
+  CHECK(compiler_filter_string != nullptr);
+  CompilerOptions::CompilerFilter compiler_filter = CompilerOptions::kDefaultCompilerFilter;
+  if (strcmp(compiler_filter_string, "interpret-only") == 0) {
+    compiler_filter = CompilerOptions::kInterpretOnly;
+  } else if (strcmp(compiler_filter_string, "space") == 0) {
+    compiler_filter = CompilerOptions::kSpace;
+  } else if (strcmp(compiler_filter_string, "balanced") == 0) {
+    compiler_filter = CompilerOptions::kBalanced;
+  } else if (strcmp(compiler_filter_string, "speed") == 0) {
+    compiler_filter = CompilerOptions::kSpeed;
+  } else if (strcmp(compiler_filter_string, "everything") == 0) {
+    compiler_filter = CompilerOptions::kEverything;
+  } else {
+    Usage("Unknown --compiler-filter value %s", compiler_filter_string);
+  }
+
+  CompilerOptions compiler_options(compiler_filter,
+                                   huge_method_threshold,
+                                   large_method_threshold,
+                                   small_method_threshold,
+                                   tiny_method_threshold,
+                                   num_dex_methods_threshold
+#ifdef ART_SEA_IR_MODE
+                                   , compiler_options.sea_ir_ = true;
+#endif
+                                   );  // NOLINT(whitespace/parens)
+
   // Done with usage checks, enable watchdog if requested
   WatchDog watch_dog(watch_dog_enabled);
 
@@ -940,22 +1073,9 @@
   }
 
   timings.StartSplit("dex2oat Setup");
-  LOG(INFO) << "dex2oat: " << oat_location;
+  LOG(INFO) << "dex2oat: " << CommandLine();
 
-  if (image) {
-    bool has_compiler_filter = false;
-    for (const char* r : runtime_args) {
-      if (strncmp(r, "-compiler-filter:", 17) == 0) {
-        has_compiler_filter = true;
-        break;
-      }
-    }
-    if (!has_compiler_filter) {
-      runtime_args.push_back("-compiler-filter:everything");
-    }
-  }
-
-  Runtime::Options options;
+  Runtime::Options runtime_options;
   std::vector<const DexFile*> boot_class_path;
   if (boot_image_option.empty()) {
     size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path);
@@ -963,24 +1083,33 @@
       LOG(ERROR) << "Failed to open some dex files: " << failure_count;
       return EXIT_FAILURE;
     }
-    options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+    runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
   } else {
-    options.push_back(std::make_pair(boot_image_option.c_str(), reinterpret_cast<void*>(NULL)));
+    runtime_options.push_back(std::make_pair(boot_image_option.c_str(),
+                                             reinterpret_cast<void*>(NULL)));
   }
   if (host_prefix.get() != NULL) {
-    options.push_back(std::make_pair("host-prefix", host_prefix->c_str()));
+    runtime_options.push_back(std::make_pair("host-prefix", host_prefix->c_str()));
   }
   for (size_t i = 0; i < runtime_args.size(); i++) {
-    options.push_back(std::make_pair(runtime_args[i], reinterpret_cast<void*>(NULL)));
+    runtime_options.push_back(std::make_pair(runtime_args[i], reinterpret_cast<void*>(NULL)));
   }
 
-#ifdef ART_SEA_IR_MODE
-  options.push_back(std::make_pair("-sea_ir", reinterpret_cast<void*>(NULL)));
-#endif
+  VerificationResults verification_results(&compiler_options);
+  DexFileToMethodInlinerMap method_inliner_map;
+  CompilerCallbacksImpl callbacks(&verification_results, &method_inliner_map);
+  runtime_options.push_back(std::make_pair("compilercallbacks", &callbacks));
 
   Dex2Oat* p_dex2oat;
-  if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set,
-      instruction_set_features, thread_count)) {
+  if (!Dex2Oat::Create(&p_dex2oat,
+                       runtime_options,
+                       compiler_options,
+                       compiler_backend,
+                       instruction_set,
+                       instruction_set_features,
+                       &verification_results,
+                       &method_inliner_map,
+                       thread_count)) {
     LOG(ERROR) << "Failed to create dex2oat";
     return EXIT_FAILURE;
   }
@@ -1050,7 +1179,8 @@
         std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i));
         UniquePtr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
         if (tmp_file.get() == nullptr) {
-            PLOG(ERROR) << "Failed to open file " << tmp_file_name << ". Try: adb shell chmod 777 /data/local/tmp";
+            PLOG(ERROR) << "Failed to open file " << tmp_file_name
+                        << ". Try: adb shell chmod 777 /data/local/tmp";
             continue;
         }
         tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
@@ -1070,15 +1200,15 @@
    * If we're not in interpret-only mode, go ahead and compile small applications. Don't
    * bother to check if we're doing the image.
    */
-  if (!image && (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly)) {
+  if (!image && (compiler_options.GetCompilerFilter() != CompilerOptions::kInterpretOnly)) {
     size_t num_methods = 0;
     for (size_t i = 0; i != dex_files.size(); ++i) {
       const DexFile* dex_file = dex_files[i];
       CHECK(dex_file != NULL);
       num_methods += dex_file->NumMethodIds();
     }
-    if (num_methods <= Runtime::Current()->GetNumDexMethodsThreshold()) {
-      Runtime::Current()->SetCompilerFilter(Runtime::kSpeed);
+    if (num_methods <= compiler_options.GetNumDexMethodsThreshold()) {
+      compiler_options.SetCompilerFilter(CompilerOptions::kSpeed);
       VLOG(compiler) << "Below method threshold, compiling anyways";
     }
   }
@@ -1222,13 +1352,13 @@
 
   // Everything was successfully written, do an explicit exit here to avoid running Runtime
   // destructors that take time (bug 10645725) unless we're a debug build or running on valgrind.
-  if (!kIsDebugBuild || (RUNNING_ON_VALGRIND == 0)) {
+  if (!kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
     dex2oat->LogCompletionTime();
     exit(EXIT_SUCCESS);
   }
 
   return EXIT_SUCCESS;
-}
+}  // NOLINT(readability/fn_size)
 }  // namespace art
 
 int main(int argc, char** argv) {
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 2a0d826..0d9b991 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -83,6 +83,11 @@
           "  --output=<file> may be used to send the output to a file.\n"
           "      Example: --output=/tmp/oatdump.txt\n"
           "\n");
+  fprintf(stderr,
+          "  --dump:[raw_mapping_table|raw_gc_map]\n"
+          "    Example: --dump:raw_gc_map\n"
+          "    Default: neither\n"
+          "\n");
   exit(EXIT_FAILURE);
 }
 
@@ -100,10 +105,12 @@
 
 class OatDumper {
  public:
-  explicit OatDumper(const std::string& host_prefix, const OatFile& oat_file)
+  explicit OatDumper(const std::string& host_prefix, const OatFile& oat_file, bool dump_raw_mapping_table, bool dump_raw_gc_map)
     : host_prefix_(host_prefix),
       oat_file_(oat_file),
       oat_dex_files_(oat_file.GetOatDexFiles()),
+      dump_raw_mapping_table_(dump_raw_mapping_table),
+      dump_raw_gc_map_(dump_raw_gc_map),
       disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet())) {
     AddAllOffsets();
   }
@@ -358,16 +365,14 @@
       DumpVmap(indent2_os, oat_method);
       indent2_os << StringPrintf("mapping_table: %p (offset=0x%08x)\n",
                                  oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
-      const bool kDumpRawMappingTable = false;
-      if (kDumpRawMappingTable) {
+      if (dump_raw_mapping_table_) {
         Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
         std::ostream indent3_os(&indent3_filter);
         DumpMappingTable(indent3_os, oat_method);
       }
       indent2_os << StringPrintf("gc_map: %p (offset=0x%08x)\n",
                                  oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
-      const bool kDumpRawGcMap = false;
-      if (kDumpRawGcMap) {
+      if (dump_raw_gc_map_) {
         Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
         std::ostream indent3_os(&indent3_filter);
         DumpGcMap(indent3_os, oat_method, code_item);
@@ -699,6 +704,8 @@
   const std::string host_prefix_;
   const OatFile& oat_file_;
   std::vector<const OatFile::OatDexFile*> oat_dex_files_;
+  bool dump_raw_mapping_table_;
+  bool dump_raw_gc_map_;
   std::set<uintptr_t> offsets_;
   UniquePtr<Disassembler> disassembler_;
 };
@@ -707,9 +714,12 @@
  public:
   explicit ImageDumper(std::ostream* os, const std::string& image_filename,
                        const std::string& host_prefix, gc::space::ImageSpace& image_space,
-                       const ImageHeader& image_header)
+                       const ImageHeader& image_header, bool dump_raw_mapping_table,
+                       bool dump_raw_gc_map)
       : os_(os), image_filename_(image_filename), host_prefix_(host_prefix),
-        image_space_(image_space), image_header_(image_header) {}
+        image_space_(image_space), image_header_(image_header),
+        dump_raw_mapping_table_(dump_raw_mapping_table),
+        dump_raw_gc_map_(dump_raw_gc_map) {}
 
   void Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     std::ostream& os = *os_;
@@ -791,7 +801,8 @@
 
     stats_.oat_file_bytes = oat_file->Size();
 
-    oat_dumper_.reset(new OatDumper(host_prefix_, *oat_file));
+    oat_dumper_.reset(new OatDumper(host_prefix_, *oat_file, dump_raw_mapping_table_,
+        dump_raw_gc_map_));
 
     for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
       CHECK(oat_dex_file != NULL);
@@ -1406,6 +1417,8 @@
   const std::string host_prefix_;
   gc::space::ImageSpace& image_space_;
   const ImageHeader& image_header_;
+  bool dump_raw_mapping_table_;
+  bool dump_raw_gc_map_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageDumper);
 };
@@ -1429,6 +1442,8 @@
   UniquePtr<std::string> host_prefix;
   std::ostream* os = &std::cout;
   UniquePtr<std::ofstream> out;
+  bool dump_raw_mapping_table = false;
+  bool dump_raw_gc_map = false;
 
   for (int i = 0; i < argc; i++) {
     const StringPiece option(argv[i]);
@@ -1440,6 +1455,15 @@
       boot_image_filename = option.substr(strlen("--boot-image=")).data();
     } else if (option.starts_with("--host-prefix=")) {
       host_prefix.reset(new std::string(option.substr(strlen("--host-prefix=")).data()));
+    } else if (option.starts_with("--dump:")) {
+        if (option == "--dump:raw_mapping_table") {
+          dump_raw_mapping_table = true;
+        } else if (option == "--dump:raw_gc_map") {
+          dump_raw_gc_map = true;
+        } else {
+          fprintf(stderr, "Unknown argument %s\n", option.data());
+          usage();
+        }
     } else if (option.starts_with("--output=")) {
       const char* filename = option.substr(strlen("--output=")).data();
       out.reset(new std::ofstream(filename));
@@ -1481,7 +1505,7 @@
       fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
       return EXIT_FAILURE;
     }
-    OatDumper oat_dumper(*host_prefix.get(), *oat_file);
+    OatDumper oat_dumper(*host_prefix.get(), *oat_file, dump_raw_mapping_table, dump_raw_gc_map);
     oat_dumper.Dump(*os);
     return EXIT_SUCCESS;
   }
@@ -1533,7 +1557,8 @@
     fprintf(stderr, "Invalid image header %s\n", image_filename);
     return EXIT_FAILURE;
   }
-  ImageDumper image_dumper(os, image_filename, *host_prefix.get(), *image_space, image_header);
+  ImageDumper image_dumper(os, image_filename, *host_prefix.get(), *image_space, image_header,
+    dump_raw_mapping_table, dump_raw_gc_map);
   image_dumper.Dump();
   return EXIT_SUCCESS;
 }
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 66c24b5..6ef0082 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -23,10 +23,40 @@
 #include "mirror/dex_cache.h"
 #include "mirror/iftable.h"
 #include "mirror/object_array.h"
+#include "object_utils.h"
 #include "sirt_ref.h"
 
 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) {
+  SirtRef<mirror::ClassLoader> class_loader(self, nullptr);
+  return FindClass(self, descriptor, class_loader);
+}
+
+inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, mirror::Class* element_class) {
+  for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
+    // Read the cached the array class once to avoid races with other threads setting it.
+    mirror::Class* array_class = find_array_class_cache_[i];
+    if (array_class != nullptr && array_class->GetComponentType() == element_class) {
+      return array_class;
+    }
+  }
+  std::string descriptor("[");
+  descriptor += ClassHelper(element_class).GetDescriptor();
+  SirtRef<mirror::ClassLoader> class_loader(self, element_class->GetClassLoader());
+  mirror::Class* array_class = FindClass(self, descriptor.c_str(), class_loader);
+  // Benign races in storing array class and incrementing index.
+  size_t victim_index = find_array_class_cache_next_victim_;
+  find_array_class_cache_[victim_index] = array_class;
+  find_array_class_cache_next_victim_ = (victim_index + 1) % kFindArrayCacheSize;
+  return array_class;
+}
+
 inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx,
                                                   mirror::ArtMethod* referrer) {
   mirror::String* resolved_string = referrer->GetDexCacheStrings()->Get(string_idx);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 48ec5ab..6897f8a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -176,17 +176,19 @@
     : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel),
       dex_cache_image_class_lookup_required_(false),
       failed_dex_cache_class_lookups_(0),
-      class_roots_(NULL),
-      array_iftable_(NULL),
+      class_roots_(nullptr),
+      array_iftable_(nullptr),
+      find_array_class_cache_next_victim_(0),
       init_done_(false),
       dex_caches_dirty_(false),
       class_table_dirty_(false),
       intern_table_(intern_table),
-      portable_resolution_trampoline_(NULL),
-      quick_resolution_trampoline_(NULL),
-      portable_imt_conflict_trampoline_(NULL),
-      quick_imt_conflict_trampoline_(NULL) {
+      portable_resolution_trampoline_(nullptr),
+      quick_resolution_trampoline_(nullptr),
+      portable_imt_conflict_trampoline_(nullptr),
+      quick_imt_conflict_trampoline_(nullptr) {
   CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
+  memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
 }
 
 void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class_path) {
@@ -335,54 +337,54 @@
 
   // Object, String and DexCache need to be rerun through FindSystemClass to finish init
   java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self);
-  mirror::Class* Object_class = FindSystemClass("Ljava/lang/Object;");
+  mirror::Class* Object_class = FindSystemClass(self, "Ljava/lang/Object;");
   CHECK_EQ(java_lang_Object.get(), Object_class);
   CHECK_EQ(java_lang_Object->GetObjectSize(), sizeof(mirror::Object));
   java_lang_String->SetStatus(mirror::Class::kStatusNotReady, self);
-  mirror::Class* String_class = FindSystemClass("Ljava/lang/String;");
+  mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;");
   CHECK_EQ(java_lang_String.get(), String_class);
   CHECK_EQ(java_lang_String->GetObjectSize(), sizeof(mirror::String));
   java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady, self);
-  mirror::Class* DexCache_class = FindSystemClass("Ljava/lang/DexCache;");
+  mirror::Class* DexCache_class = FindSystemClass(self, "Ljava/lang/DexCache;");
   CHECK_EQ(java_lang_String.get(), String_class);
   CHECK_EQ(java_lang_DexCache.get(), DexCache_class);
   CHECK_EQ(java_lang_DexCache->GetObjectSize(), sizeof(mirror::DexCache));
 
   // Setup the primitive array type classes - can't be done until Object has a vtable.
-  SetClassRoot(kBooleanArrayClass, FindSystemClass("[Z"));
+  SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z"));
   mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
 
-  SetClassRoot(kByteArrayClass, FindSystemClass("[B"));
+  SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B"));
   mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
 
-  mirror::Class* found_char_array_class = FindSystemClass("[C");
+  mirror::Class* found_char_array_class = FindSystemClass(self, "[C");
   CHECK_EQ(char_array_class.get(), found_char_array_class);
 
-  SetClassRoot(kShortArrayClass, FindSystemClass("[S"));
+  SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S"));
   mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
 
-  mirror::Class* found_int_array_class = FindSystemClass("[I");
+  mirror::Class* found_int_array_class = FindSystemClass(self, "[I");
   CHECK_EQ(int_array_class.get(), found_int_array_class);
 
-  SetClassRoot(kLongArrayClass, FindSystemClass("[J"));
+  SetClassRoot(kLongArrayClass, FindSystemClass(self, "[J"));
   mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass));
 
-  SetClassRoot(kFloatArrayClass, FindSystemClass("[F"));
+  SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F"));
   mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
 
-  SetClassRoot(kDoubleArrayClass, FindSystemClass("[D"));
+  SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D"));
   mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
 
-  mirror::Class* found_class_array_class = FindSystemClass("[Ljava/lang/Class;");
+  mirror::Class* found_class_array_class = FindSystemClass(self, "[Ljava/lang/Class;");
   CHECK_EQ(class_array_class.get(), found_class_array_class);
 
-  mirror::Class* found_object_array_class = FindSystemClass("[Ljava/lang/Object;");
+  mirror::Class* found_object_array_class = FindSystemClass(self, "[Ljava/lang/Object;");
   CHECK_EQ(object_array_class.get(), found_object_array_class);
 
   // Setup the single, global copy of "iftable".
-  mirror::Class* java_lang_Cloneable = FindSystemClass("Ljava/lang/Cloneable;");
+  mirror::Class* java_lang_Cloneable = FindSystemClass(self, "Ljava/lang/Cloneable;");
   CHECK(java_lang_Cloneable != NULL);
-  mirror::Class* java_io_Serializable = FindSystemClass("Ljava/io/Serializable;");
+  mirror::Class* java_io_Serializable = FindSystemClass(self, "Ljava/io/Serializable;");
   CHECK(java_io_Serializable != NULL);
   // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to
   // crawl up and explicitly list all of the supers as well.
@@ -398,73 +400,73 @@
   CHECK_EQ(java_io_Serializable, kh.GetDirectInterface(1));
   // Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their
   // dex_cache_ fields and register them in class_table_.
-  mirror::Class* Class_class = FindSystemClass("Ljava/lang/Class;");
+  mirror::Class* Class_class = FindSystemClass(self, "Ljava/lang/Class;");
   CHECK_EQ(java_lang_Class.get(), Class_class);
 
   java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusNotReady, self);
-  mirror::Class* Art_method_class = FindSystemClass("Ljava/lang/reflect/ArtMethod;");
+  mirror::Class* Art_method_class = FindSystemClass(self, "Ljava/lang/reflect/ArtMethod;");
   CHECK_EQ(java_lang_reflect_ArtMethod.get(), Art_method_class);
 
   java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusNotReady, self);
-  mirror::Class* Art_field_class = FindSystemClass("Ljava/lang/reflect/ArtField;");
+  mirror::Class* Art_field_class = FindSystemClass(self, "Ljava/lang/reflect/ArtField;");
   CHECK_EQ(java_lang_reflect_ArtField.get(), Art_field_class);
 
-  mirror::Class* String_array_class = FindSystemClass(class_roots_descriptors_[kJavaLangStringArrayClass]);
+  mirror::Class* String_array_class = FindSystemClass(self, class_roots_descriptors_[kJavaLangStringArrayClass]);
   CHECK_EQ(object_array_string.get(), String_array_class);
 
   mirror::Class* Art_method_array_class =
-      FindSystemClass(class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]);
+      FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]);
   CHECK_EQ(object_array_art_method.get(), Art_method_array_class);
 
   mirror::Class* Art_field_array_class =
-      FindSystemClass(class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]);
+      FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]);
   CHECK_EQ(object_array_art_field.get(), Art_field_array_class);
 
   // End of special init trickery, subsequent classes may be loaded via FindSystemClass.
 
   // Create java.lang.reflect.Proxy root.
-  mirror::Class* java_lang_reflect_Proxy = FindSystemClass("Ljava/lang/reflect/Proxy;");
+  mirror::Class* java_lang_reflect_Proxy = FindSystemClass(self, "Ljava/lang/reflect/Proxy;");
   SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy);
 
   // java.lang.ref classes need to be specially flagged, but otherwise are normal classes
-  mirror::Class* java_lang_ref_Reference = FindSystemClass("Ljava/lang/ref/Reference;");
+  mirror::Class* java_lang_ref_Reference = FindSystemClass(self, "Ljava/lang/ref/Reference;");
   SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference);
-  mirror::Class* java_lang_ref_FinalizerReference = FindSystemClass("Ljava/lang/ref/FinalizerReference;");
+  mirror::Class* java_lang_ref_FinalizerReference = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
   java_lang_ref_FinalizerReference->SetAccessFlags(
       java_lang_ref_FinalizerReference->GetAccessFlags() |
           kAccClassIsReference | kAccClassIsFinalizerReference);
-  mirror::Class* java_lang_ref_PhantomReference = FindSystemClass("Ljava/lang/ref/PhantomReference;");
+  mirror::Class* java_lang_ref_PhantomReference = FindSystemClass(self, "Ljava/lang/ref/PhantomReference;");
   java_lang_ref_PhantomReference->SetAccessFlags(
       java_lang_ref_PhantomReference->GetAccessFlags() |
           kAccClassIsReference | kAccClassIsPhantomReference);
-  mirror::Class* java_lang_ref_SoftReference = FindSystemClass("Ljava/lang/ref/SoftReference;");
+  mirror::Class* java_lang_ref_SoftReference = FindSystemClass(self, "Ljava/lang/ref/SoftReference;");
   java_lang_ref_SoftReference->SetAccessFlags(
       java_lang_ref_SoftReference->GetAccessFlags() | kAccClassIsReference);
-  mirror::Class* java_lang_ref_WeakReference = FindSystemClass("Ljava/lang/ref/WeakReference;");
+  mirror::Class* java_lang_ref_WeakReference = FindSystemClass(self, "Ljava/lang/ref/WeakReference;");
   java_lang_ref_WeakReference->SetAccessFlags(
       java_lang_ref_WeakReference->GetAccessFlags() |
           kAccClassIsReference | kAccClassIsWeakReference);
 
   // Setup the ClassLoader, verifying the object_size_.
-  mirror::Class* java_lang_ClassLoader = FindSystemClass("Ljava/lang/ClassLoader;");
+  mirror::Class* java_lang_ClassLoader = FindSystemClass(self, "Ljava/lang/ClassLoader;");
   CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), sizeof(mirror::ClassLoader));
   SetClassRoot(kJavaLangClassLoader, java_lang_ClassLoader);
 
   // Set up java.lang.Throwable, java.lang.ClassNotFoundException, and
   // java.lang.StackTraceElement as a convenience.
-  SetClassRoot(kJavaLangThrowable, FindSystemClass("Ljava/lang/Throwable;"));
+  SetClassRoot(kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;"));
   mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
-  SetClassRoot(kJavaLangClassNotFoundException, FindSystemClass("Ljava/lang/ClassNotFoundException;"));
-  SetClassRoot(kJavaLangStackTraceElement, FindSystemClass("Ljava/lang/StackTraceElement;"));
-  SetClassRoot(kJavaLangStackTraceElementArrayClass, FindSystemClass("[Ljava/lang/StackTraceElement;"));
+  SetClassRoot(kJavaLangClassNotFoundException, FindSystemClass(self, "Ljava/lang/ClassNotFoundException;"));
+  SetClassRoot(kJavaLangStackTraceElement, FindSystemClass(self, "Ljava/lang/StackTraceElement;"));
+  SetClassRoot(kJavaLangStackTraceElementArrayClass, FindSystemClass(self, "[Ljava/lang/StackTraceElement;"));
   mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
 
-  FinishInit();
+  FinishInit(self);
 
   VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
 }
 
-void ClassLinker::FinishInit() {
+void ClassLinker::FinishInit(Thread* self) {
   VLOG(startup) << "ClassLinker::FinishInit entering";
 
   // Let the heap know some key offsets into java.lang.ref instances
@@ -473,7 +475,7 @@
   // fully initialized
   mirror::Class* java_lang_ref_Reference = GetClassRoot(kJavaLangRefReference);
   mirror::Class* java_lang_ref_FinalizerReference =
-      FindSystemClass("Ljava/lang/ref/FinalizerReference;");
+      FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
 
   mirror::ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
   FieldHelper fh(pendingNext);
@@ -550,101 +552,41 @@
   const char* class_path = Runtime::Current()->GetClassPathString().c_str();
 
   gc::Heap* heap = Runtime::Current()->GetHeap();
-  std::string boot_image_option_string("--boot-image=");
-  boot_image_option_string += heap->GetImageSpace()->GetImageFilename();
-  const char* boot_image_option = boot_image_option_string.c_str();
+  std::string boot_image_option("--boot-image=");
+  boot_image_option += heap->GetImageSpace()->GetImageFilename();
 
-  std::string dex_file_option_string("--dex-file=");
-  dex_file_option_string += dex_filename;
-  const char* dex_file_option = dex_file_option_string.c_str();
+  std::string dex_file_option("--dex-file=");
+  dex_file_option += dex_filename;
 
-  std::string oat_fd_option_string("--oat-fd=");
-  StringAppendF(&oat_fd_option_string, "%d", oat_fd);
-  const char* oat_fd_option = oat_fd_option_string.c_str();
+  std::string oat_fd_option("--oat-fd=");
+  StringAppendF(&oat_fd_option, "%d", oat_fd);
 
-  std::string oat_location_option_string("--oat-location=");
-  oat_location_option_string += oat_cache_filename;
-  const char* oat_location_option = oat_location_option_string.c_str();
+  std::string oat_location_option("--oat-location=");
+  oat_location_option += oat_cache_filename;
 
-  std::string oat_compiler_filter_string("-compiler-filter:");
-  Runtime::CompilerFilter filter = Runtime::Current()->GetCompilerFilter();
-  switch (filter) {
-    case Runtime::kInterpretOnly:
-      oat_compiler_filter_string += "interpret-only";
-      break;
-    case Runtime::kSpace:
-      oat_compiler_filter_string += "space";
-      break;
-    case Runtime::kBalanced:
-      oat_compiler_filter_string += "balanced";
-      break;
-    case Runtime::kSpeed:
-      oat_compiler_filter_string += "speed";
-      break;
-    case Runtime::kEverything:
-      oat_compiler_filter_string += "everything";
-      break;
-    default:
-      LOG(FATAL) << "Unexpected case: " << filter;
+  std::vector<std::string> argv;
+  argv.push_back(dex2oat);
+  argv.push_back("--runtime-arg");
+  argv.push_back("-Xms64m");
+  argv.push_back("--runtime-arg");
+  argv.push_back("-Xmx64m");
+  argv.push_back("--runtime-arg");
+  argv.push_back("-classpath");
+  argv.push_back("--runtime-arg");
+  argv.push_back(class_path);
+  if (!kIsTargetBuild) {
+    argv.push_back("--host");
   }
-  const char* oat_compiler_filter_option = oat_compiler_filter_string.c_str();
-
-  // fork and exec dex2oat
-  pid_t pid = fork();
-  if (pid == 0) {
-    // no allocation allowed between fork and exec
-
-    // change process groups, so we don't get reaped by ProcessManager
-    setpgid(0, 0);
-
-    // gLogVerbosity.class_linker = true;
-    VLOG(class_linker) << dex2oat
-                       << " --runtime-arg -Xms64m"
-                       << " --runtime-arg -Xmx64m"
-                       << " --runtime-arg -classpath"
-                       << " --runtime-arg " << class_path
-                       << " --runtime-arg " << oat_compiler_filter_option
-#if !defined(ART_TARGET)
-                       << " --host"
-#endif
-                       << " " << boot_image_option
-                       << " " << dex_file_option
-                       << " " << oat_fd_option
-                       << " " << oat_location_option;
-
-    execl(dex2oat, dex2oat,
-          "--runtime-arg", "-Xms64m",
-          "--runtime-arg", "-Xmx64m",
-          "--runtime-arg", "-classpath",
-          "--runtime-arg", class_path,
-          "--runtime-arg", oat_compiler_filter_option,
-#if !defined(ART_TARGET)
-          "--host",
-#endif
-          boot_image_option,
-          dex_file_option,
-          oat_fd_option,
-          oat_location_option,
-          NULL);
-
-    PLOG(FATAL) << "execl(" << dex2oat << ") failed";
-    return false;
-  } else {
-    // wait for dex2oat to finish
-    int status;
-    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-    if (got_pid != pid) {
-      *error_msg = StringPrintf("Failed to create oat file. Waitpid failed: wanted %d, got %d",
-                                pid, got_pid);
-      return false;
-    }
-    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-      *error_msg = StringPrintf("Failed to create oat file. %s failed with dex-file '%s'",
-                                dex2oat, dex_filename);
-      return false;
-    }
+  argv.push_back(boot_image_option);
+  argv.push_back(dex_file_option);
+  argv.push_back(oat_fd_option);
+  argv.push_back(oat_location_option);
+  const std::vector<std::string>& compiler_options = Runtime::Current()->GetCompilerOptions();
+  for (size_t i = 0; compiler_options.size(); ++i) {
+    argv.push_back(compiler_options[i].c_str());
   }
-  return true;
+
+  return Exec(argv, error_msg);
 }
 
 const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
@@ -1111,7 +1053,7 @@
   mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
   mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
 
-  FinishInit();
+  FinishInit(self);
 
   VLOG(startup) << "ClassLinker::InitFromImage exiting";
 }
@@ -1150,6 +1092,13 @@
   }
   callback(reinterpret_cast<mirror::Object**>(&array_iftable_), arg, 0, kRootVMInternal);
   DCHECK(array_iftable_ != nullptr);
+  for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
+    if (find_array_class_cache_[i] != nullptr) {
+      callback(reinterpret_cast<mirror::Object**>(&find_array_class_cache_[i]), arg, 0,
+               kRootVMInternal);
+      DCHECK(find_array_class_cache_[i] != nullptr);
+    }
+  }
 }
 
 void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) {
@@ -1307,21 +1256,10 @@
   return klass;
 }
 
-bool ClassLinker::IsInBootClassPath(const char* descriptor) {
-  DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
-  return pair.second != NULL;
-}
-
-mirror::Class* ClassLinker::FindSystemClass(const char* descriptor) {
-  SirtRef<mirror::ClassLoader> class_loader(Thread::Current(), nullptr);
-  return FindClass(descriptor, class_loader);
-}
-
-mirror::Class* ClassLinker::FindClass(const char* descriptor,
+mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
                                       const SirtRef<mirror::ClassLoader>& class_loader) {
   DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
-  Thread* self = Thread::Current();
-  DCHECK(self != NULL);
+  DCHECK(self != nullptr);
   self->AssertNoPendingException();
   if (descriptor[1] == '\0') {
     // only the descriptors of primitive types should be 1 character long, also avoid class lookup
@@ -1335,7 +1273,7 @@
   }
   // Class is not yet loaded.
   if (descriptor[0] == '[') {
-    return CreateArrayClass(descriptor, class_loader);
+    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) {
@@ -1346,7 +1284,7 @@
     // 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(descriptor);
+      mirror::Class* system_class = FindSystemClass(self, descriptor);
       CHECK(system_class != NULL);
       return system_class;
     }
@@ -1365,7 +1303,7 @@
     }
 
   } else {
-    ScopedObjectAccessUnchecked soa(self->GetJniEnv());
+    ScopedObjectAccessUnchecked soa(self);
     ScopedLocalRef<jobject> class_loader_object(soa.Env(),
                                                 soa.AddLocalReference<jobject>(class_loader.get()));
     std::string class_name_string(DescriptorToDot(descriptor));
@@ -1382,7 +1320,7 @@
                                                WellKnownClasses::java_lang_ClassLoader_loadClass,
                                                class_name_object.get()));
     }
-    if (soa.Self()->IsExceptionPending()) {
+    if (self->IsExceptionPending()) {
       // If the ClassLoader threw, pass that exception up.
       return NULL;
     } else if (result.get() == NULL) {
@@ -2133,12 +2071,11 @@
 // array class; that always comes from the base element class.
 //
 // Returns NULL with an exception raised on failure.
-mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor,
+mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor,
                                              const SirtRef<mirror::ClassLoader>& class_loader) {
   // Identify the underlying component type
   CHECK_EQ('[', descriptor[0]);
-  Thread* self = Thread::Current();
-  SirtRef<mirror::Class> component_type(self, FindClass(descriptor + 1, class_loader));
+  SirtRef<mirror::Class> component_type(self, FindClass(self, descriptor + 1, class_loader));
   if (component_type.get() == nullptr) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
@@ -3242,7 +3179,7 @@
     for (int i = super->GetVTable()->GetLength() - 1; i >= 0; --i) {
       mirror::ArtMethod* method = klass->GetVTable()->Get(i);
       if (method != super->GetVTable()->Get(i) &&
-          !IsSameMethodSignatureInDifferentClassContexts(method, super.get(), klass.get())) {
+          !IsSameMethodSignatureInDifferentClassContexts(self, method, super.get(), klass.get())) {
         ThrowLinkageError(klass.get(), "Class %s method %s resolves differently in superclass %s",
                           PrettyDescriptor(klass.get()).c_str(), PrettyMethod(method).c_str(),
                           PrettyDescriptor(super.get()).c_str());
@@ -3255,7 +3192,7 @@
     if (klass->GetClassLoader() != interface->GetClassLoader()) {
       for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) {
         mirror::ArtMethod* method = klass->GetIfTable()->GetMethodArray(i)->Get(j);
-        if (!IsSameMethodSignatureInDifferentClassContexts(method, interface.get(),
+        if (!IsSameMethodSignatureInDifferentClassContexts(self, method, interface.get(),
                                                            method->GetDeclaringClass())) {
           ThrowLinkageError(klass.get(), "Class %s method %s resolves differently in interface %s",
                             PrettyDescriptor(method->GetDeclaringClass()).c_str(),
@@ -3271,13 +3208,13 @@
 
 // Returns true if classes referenced by the signature of the method are the
 // same classes in klass1 as they are in klass2.
-bool ClassLinker::IsSameMethodSignatureInDifferentClassContexts(mirror::ArtMethod* method,
+bool ClassLinker::IsSameMethodSignatureInDifferentClassContexts(Thread* self,
+                                                                mirror::ArtMethod* method,
                                                                 mirror::Class* klass1,
                                                                 mirror::Class* klass2) {
   if (klass1 == klass2) {
     return true;
   }
-  Thread* self = Thread::Current();
   CHECK(klass1 != nullptr);
   CHECK(klass2 != nullptr);
   SirtRef<mirror::ClassLoader> loader1(self, klass1->GetClassLoader());
@@ -3292,7 +3229,7 @@
     }
     if (descriptor[0] == 'L' || descriptor[0] == '[') {
       // Found a non-primitive type.
-      if (!IsSameDescriptorInDifferentClassContexts(descriptor, loader1, loader2)) {
+      if (!IsSameDescriptorInDifferentClassContexts(self, descriptor, loader1, loader2)) {
         return false;
       }
     }
@@ -3300,7 +3237,7 @@
   // Check the return type
   const char* descriptor = dex_file.GetReturnTypeDescriptor(proto_id);
   if (descriptor[0] == 'L' || descriptor[0] == '[') {
-    if (!IsSameDescriptorInDifferentClassContexts(descriptor, loader1, loader2)) {
+    if (!IsSameDescriptorInDifferentClassContexts(self, descriptor, loader1, loader2)) {
       return false;
     }
   }
@@ -3308,16 +3245,15 @@
 }
 
 // Returns true if the descriptor resolves to the same class in the context of loader1 and loader2.
-bool ClassLinker::IsSameDescriptorInDifferentClassContexts(const char* descriptor,
+bool ClassLinker::IsSameDescriptorInDifferentClassContexts(Thread* self, const char* descriptor,
                                                            SirtRef<mirror::ClassLoader>& loader1,
                                                            SirtRef<mirror::ClassLoader>& loader2) {
   CHECK(descriptor != nullptr);
-  Thread* self = Thread::Current();
-  SirtRef<mirror::Class> found1(self, FindClass(descriptor, loader1));
+  SirtRef<mirror::Class> found1(self, FindClass(self, descriptor, loader1));
   if (found1.get() == nullptr) {
     self->ClearException();
   }
-  mirror::Class* found2 = FindClass(descriptor, loader2);
+  mirror::Class* found2 = FindClass(self, descriptor, loader2);
   if (found2 == nullptr) {
     self->ClearException();
   }
@@ -4117,22 +4053,22 @@
   DCHECK(dex_cache.get() != NULL);
   mirror::Class* resolved = dex_cache->GetResolvedType(type_idx);
   if (resolved == NULL) {
+    Thread* self = Thread::Current();
     const char* descriptor = dex_file.StringByTypeIdx(type_idx);
-    resolved = FindClass(descriptor, class_loader);
+    resolved = FindClass(self, descriptor, class_loader);
     if (resolved != NULL) {
       // TODO: we used to throw here if resolved's class loader was not the
       //       boot class loader. This was to permit different classes with the
       //       same name to be loaded simultaneously by different loaders
       dex_cache->SetResolvedType(type_idx, resolved);
     } else {
-      Thread* self = Thread::Current();
       CHECK(self->IsExceptionPending())
           << "Expected pending exception for failed resolution of: " << descriptor;
       // Convert a ClassNotFoundException to a NoClassDefFoundError.
       SirtRef<mirror::Throwable> cause(self, self->GetException(NULL));
       if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) {
         DCHECK(resolved == NULL);  // No SirtRef needed to preserve resolved.
-        Thread::Current()->ClearException();
+        self->ClearException();
         ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor);
         self->GetException(NULL)->SetCause(cause.get());
       }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 0745ee2..f346102 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -72,10 +72,17 @@
 
   // Finds a class by its descriptor, loading it if necessary.
   // If class_loader is null, searches boot_class_path_.
-  mirror::Class* FindClass(const char* descriptor, const SirtRef<mirror::ClassLoader>& class_loader)
+  mirror::Class* FindClass(Thread* self, const char* descriptor,
+                           const SirtRef<mirror::ClassLoader>& class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  mirror::Class* FindSystemClass(const char* descriptor)
+  // Finds a class by its descriptor using the "system" class loader, ie by searching the
+  // boot_class_path_.
+  mirror::Class* FindSystemClass(Thread* self, const char* descriptor)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Finds the array class given for the element class.
+  mirror::Class* FindArrayClass(Thread* self, mirror::Class* element_class)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Reutrns true if the class linker is initialized.
@@ -378,7 +385,7 @@
       LOCKS_EXCLUDED(dex_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void FinishInit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void FinishInit(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // For early bootstrapping by Init
   mirror::Class* AllocClass(Thread* self, mirror::Class* java_lang_Class, size_t class_size)
@@ -399,7 +406,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 
-  mirror::Class* CreateArrayClass(const char* descriptor,
+  mirror::Class* CreateArrayClass(Thread* self, const char* descriptor,
                                   const SirtRef<mirror::ClassLoader>& class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -449,12 +456,12 @@
   bool ValidateSuperClassDescriptors(const SirtRef<mirror::Class>& klass)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsSameDescriptorInDifferentClassContexts(const char* descriptor,
+  bool IsSameDescriptorInDifferentClassContexts(Thread* self, const char* descriptor,
                                                 SirtRef<mirror::ClassLoader>& class_loader1,
                                                 SirtRef<mirror::ClassLoader>& class_loader2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsSameMethodSignatureInDifferentClassContexts(mirror::ArtMethod* method,
+  bool IsSameMethodSignatureInDifferentClassContexts(Thread* self, mirror::ArtMethod* method,
                                                      mirror::Class* klass1,
                                                      mirror::Class* klass2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -617,8 +624,15 @@
     return descriptor;
   }
 
+  // The interface table used by all arrays.
   mirror::IfTable* array_iftable_;
 
+  // A cache of the last FindArrayClass results. The cache serves to avoid creating array class
+  // descriptors for the sake of performing FindClass.
+  static constexpr size_t kFindArrayCacheSize = 16;
+  mirror::Class* find_array_class_cache_[kFindArrayCacheSize];
+  size_t find_array_class_cache_next_victim_;
+
   bool init_done_;
   bool dex_caches_dirty_ GUARDED_BY(dex_lock_);
   bool class_table_dirty_ GUARDED_BY(Locks::classlinker_classes_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index ebf02fe..d6a67cc 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -41,18 +41,20 @@
  protected:
   void AssertNonExistentClass(const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    EXPECT_TRUE(class_linker_->FindSystemClass(descriptor.c_str()) == NULL);
     Thread* self = Thread::Current();
+    EXPECT_TRUE(class_linker_->FindSystemClass(self, descriptor.c_str()) == NULL);
     EXPECT_TRUE(self->IsExceptionPending());
     mirror::Object* exception = self->GetException(NULL);
     self->ClearException();
-    mirror::Class* exception_class = class_linker_->FindSystemClass("Ljava/lang/NoClassDefFoundError;");
+    mirror::Class* exception_class =
+        class_linker_->FindSystemClass(self, "Ljava/lang/NoClassDefFoundError;");
     EXPECT_TRUE(exception->InstanceOf(exception_class));
   }
 
   void AssertPrimitiveClass(const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    AssertPrimitiveClass(descriptor, class_linker_->FindSystemClass(descriptor.c_str()));
+    Thread* self = Thread::Current();
+    AssertPrimitiveClass(descriptor, class_linker_->FindSystemClass(self, descriptor.c_str()));
   }
 
   void AssertPrimitiveClass(const std::string& descriptor, mirror::Class* primitive)
@@ -98,7 +100,7 @@
     Thread* self = Thread::Current();
     SirtRef<mirror::ClassLoader> loader(self, class_loader);
     SirtRef<mirror::Class> array(self,
-                                 class_linker_->FindClass(array_descriptor.c_str(), loader));
+                                 class_linker_->FindClass(self, array_descriptor.c_str(), loader));
     ClassHelper array_component_ch(array->GetComponentType());
     EXPECT_STREQ(component_type.c_str(), array_component_ch.GetDescriptor());
     EXPECT_EQ(class_loader, array->GetClassLoader());
@@ -115,7 +117,8 @@
     EXPECT_TRUE(array->GetClass()->GetSuperClass() != NULL);
     ASSERT_STREQ(array_descriptor.c_str(), kh.GetDescriptor());
     EXPECT_TRUE(array->GetSuperClass() != NULL);
-    EXPECT_EQ(class_linker_->FindSystemClass("Ljava/lang/Object;"), array->GetSuperClass());
+    Thread* self = Thread::Current();
+    EXPECT_EQ(class_linker_->FindSystemClass(self, "Ljava/lang/Object;"), array->GetSuperClass());
     EXPECT_TRUE(array->HasSuperClass());
     ASSERT_TRUE(array->GetComponentType() != NULL);
     kh.ChangeClass(array->GetComponentType());
@@ -147,6 +150,7 @@
     kh.ChangeClass(array.get());
     kh.ChangeClass(kh.GetDirectInterface(1));
     EXPECT_STREQ(kh.GetDescriptor(), "Ljava/io/Serializable;");
+    EXPECT_EQ(class_linker_->FindArrayClass(self, array->GetComponentType()), array.get());
   }
 
   void AssertMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -300,8 +304,8 @@
   void AssertDexFileClass(mirror::ClassLoader* class_loader, const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     ASSERT_TRUE(descriptor != NULL);
-    SirtRef<mirror::Class> klass(Thread::Current(),
-                                 class_linker_->FindSystemClass(descriptor.c_str()));
+    Thread* self = Thread::Current();
+    SirtRef<mirror::Class> klass(self, class_linker_->FindSystemClass(self, descriptor.c_str()));
     ASSERT_TRUE(klass.get() != nullptr);
     EXPECT_STREQ(descriptor.c_str(), ClassHelper(klass.get()).GetDescriptor());
     EXPECT_EQ(class_loader, klass->GetClassLoader());
@@ -359,7 +363,9 @@
   std::vector<CheckOffset> offsets;
 
   bool Check() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::Class* klass = Runtime::Current()->GetClassLinker()->FindSystemClass(class_descriptor.c_str());
+    Thread* self = Thread::Current();
+    mirror::Class* klass =
+        Runtime::Current()->GetClassLinker()->FindSystemClass(self, class_descriptor.c_str());
     CHECK(klass != NULL) << class_descriptor;
 
     bool error = false;
@@ -646,12 +652,12 @@
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Nested")));
 
-  mirror::Class* outer = class_linker_->FindClass("LNested;", class_loader);
+  mirror::Class* outer = class_linker_->FindClass(soa.Self(), "LNested;", class_loader);
   ASSERT_TRUE(outer != NULL);
   EXPECT_EQ(0U, outer->NumVirtualMethods());
   EXPECT_EQ(1U, outer->NumDirectMethods());
 
-  mirror::Class* inner = class_linker_->FindClass("LNested$Inner;", class_loader);
+  mirror::Class* inner = class_linker_->FindClass(soa.Self(), "LNested$Inner;", class_loader);
   ASSERT_TRUE(inner != NULL);
   EXPECT_EQ(0U, inner->NumVirtualMethods());
   EXPECT_EQ(1U, inner->NumDirectMethods());
@@ -673,7 +679,7 @@
 
 TEST_F(ClassLinkerTest, FindClass) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* JavaLangObject = class_linker_->FindSystemClass("Ljava/lang/Object;");
+  mirror::Class* JavaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
   ClassHelper kh(JavaLangObject);
   ASSERT_TRUE(JavaLangObject != NULL);
   ASSERT_TRUE(JavaLangObject->GetClass() != NULL);
@@ -710,7 +716,7 @@
 
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass")));
   AssertNonExistentClass("LMyClass;");
-  mirror::Class* MyClass = class_linker_->FindClass("LMyClass;", class_loader);
+  mirror::Class* MyClass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader);
   kh.ChangeClass(MyClass);
   ASSERT_TRUE(MyClass != NULL);
   ASSERT_TRUE(MyClass->GetClass() != NULL);
@@ -761,7 +767,7 @@
 // start of the object
 TEST_F(ClassLinkerTest, ValidateObjectArrayElementsOffset) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* array_class = class_linker_->FindSystemClass("[Ljava/lang/String;");
+  mirror::Class* array_class = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
   mirror::ObjectArray<mirror::String>* array =
       mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), array_class, 0);
   uintptr_t data_offset =
@@ -777,27 +783,27 @@
 TEST_F(ClassLinkerTest, ValidatePrimitiveArrayElementsOffset) {
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::LongArray> long_array(soa.Self(), mirror::LongArray::Alloc(soa.Self(), 0));
-  EXPECT_EQ(class_linker_->FindSystemClass("[J"), long_array->GetClass());
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[J"), long_array->GetClass());
   uintptr_t data_offset = reinterpret_cast<uintptr_t>(long_array->GetData());
   EXPECT_TRUE(IsAligned<8>(data_offset));  // Longs require 8 byte alignment
 
   SirtRef<mirror::DoubleArray> double_array(soa.Self(), mirror::DoubleArray::Alloc(soa.Self(), 0));
-  EXPECT_EQ(class_linker_->FindSystemClass("[D"), double_array->GetClass());
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[D"), double_array->GetClass());
   data_offset = reinterpret_cast<uintptr_t>(double_array->GetData());
   EXPECT_TRUE(IsAligned<8>(data_offset));  // Doubles require 8 byte alignment
 
   SirtRef<mirror::IntArray> int_array(soa.Self(), mirror::IntArray::Alloc(soa.Self(), 0));
-  EXPECT_EQ(class_linker_->FindSystemClass("[I"), int_array->GetClass());
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[I"), int_array->GetClass());
   data_offset = reinterpret_cast<uintptr_t>(int_array->GetData());
   EXPECT_TRUE(IsAligned<4>(data_offset));  // Ints require 4 byte alignment
 
   SirtRef<mirror::CharArray> char_array(soa.Self(), mirror::CharArray::Alloc(soa.Self(), 0));
-  EXPECT_EQ(class_linker_->FindSystemClass("[C"), char_array->GetClass());
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[C"), char_array->GetClass());
   data_offset = reinterpret_cast<uintptr_t>(char_array->GetData());
   EXPECT_TRUE(IsAligned<2>(data_offset));  // Chars require 2 byte alignment
 
   SirtRef<mirror::ShortArray> short_array(soa.Self(), mirror::ShortArray::Alloc(soa.Self(), 0));
-  EXPECT_EQ(class_linker_->FindSystemClass("[S"), short_array->GetClass());
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[S"), short_array->GetClass());
   data_offset = reinterpret_cast<uintptr_t>(short_array->GetData());
   EXPECT_TRUE(IsAligned<2>(data_offset));  // Shorts require 2 byte alignment
 
@@ -810,28 +816,28 @@
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr);
   mirror::Class* c;
-  c = class_linker_->FindClass("Ljava/lang/Boolean;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Boolean;", class_loader);
   FieldHelper fh(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Byte;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Byte;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Character;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Character;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Double;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Double;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Float;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Float;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Integer;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Integer;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Long;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Long;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Short;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Short;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
 }
@@ -840,8 +846,8 @@
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::ClassLoader> class_loader_1(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass")));
   SirtRef<mirror::ClassLoader> class_loader_2(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass")));
-  mirror::Class* MyClass_1 = class_linker_->FindClass("LMyClass;", class_loader_1);
-  mirror::Class* MyClass_2 = class_linker_->FindClass("LMyClass;", class_loader_2);
+  mirror::Class* MyClass_1 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_1);
+  mirror::Class* MyClass_2 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_2);
   EXPECT_TRUE(MyClass_1 != NULL);
   EXPECT_TRUE(MyClass_2 != NULL);
   EXPECT_NE(MyClass_1, MyClass_2);
@@ -849,8 +855,10 @@
 
 TEST_F(ClassLinkerTest, StaticFields) {
   ScopedObjectAccess soa(Thread::Current());
-  SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Statics")));
-  SirtRef<mirror::Class> statics(soa.Self(), class_linker_->FindClass("LStatics;", class_loader));
+  SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
+                                            soa.Decode<mirror::ClassLoader*>(LoadDex("Statics")));
+  SirtRef<mirror::Class> statics(soa.Self(), class_linker_->FindClass(soa.Self(), "LStatics;",
+                                                                      class_loader));
   class_linker_->EnsureInitialized(statics, true, true);
 
   // Static final primitives that are initialized by a compile-time constant
@@ -933,11 +941,11 @@
 TEST_F(ClassLinkerTest, Interfaces) {
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Interfaces")));
-  mirror::Class* I = class_linker_->FindClass("LInterfaces$I;", class_loader);
-  mirror::Class* J = class_linker_->FindClass("LInterfaces$J;", class_loader);
-  mirror::Class* K = class_linker_->FindClass("LInterfaces$K;", class_loader);
-  mirror::Class* A = class_linker_->FindClass("LInterfaces$A;", class_loader);
-  mirror::Class* B = class_linker_->FindClass("LInterfaces$B;", class_loader);
+  mirror::Class* I = class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader);
+  mirror::Class* J = class_linker_->FindClass(soa.Self(), "LInterfaces$J;", class_loader);
+  mirror::Class* K = class_linker_->FindClass(soa.Self(), "LInterfaces$K;", class_loader);
+  mirror::Class* A = class_linker_->FindClass(soa.Self(), "LInterfaces$A;", class_loader);
+  mirror::Class* B = class_linker_->FindClass(soa.Self(), "LInterfaces$B;", class_loader);
   EXPECT_TRUE(I->IsAssignableFrom(A));
   EXPECT_TRUE(J->IsAssignableFrom(A));
   EXPECT_TRUE(J->IsAssignableFrom(K));
@@ -996,7 +1004,7 @@
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
   const DexFile* dex_file = Runtime::Current()->GetCompileTimeClassPath(jclass_loader)[0];
   CHECK(dex_file != NULL);
-  mirror::Class* klass = class_linker_->FindClass("LStaticsFromCode;", class_loader);
+  mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader);
   mirror::ArtMethod* clinit = klass->FindClassInitializer();
   mirror::ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;");
   const DexFile::StringId* string_id = dex_file->FindStringId("LStaticsFromCode;");
@@ -1017,32 +1025,32 @@
   mirror::Class* c;
 
   // Object has a finalize method, but we know it's empty.
-  c = class_linker_->FindSystemClass("Ljava/lang/Object;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
   EXPECT_FALSE(c->IsFinalizable());
 
   // Enum has a finalize method to prevent its subclasses from implementing one.
-  c = class_linker_->FindSystemClass("Ljava/lang/Enum;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Enum;");
   EXPECT_FALSE(c->IsFinalizable());
 
   // RoundingMode is an enum.
-  c = class_linker_->FindSystemClass("Ljava/math/RoundingMode;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/math/RoundingMode;");
   EXPECT_FALSE(c->IsFinalizable());
 
   // RandomAccessFile extends Object and overrides finalize.
-  c = class_linker_->FindSystemClass("Ljava/io/RandomAccessFile;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/RandomAccessFile;");
   EXPECT_TRUE(c->IsFinalizable());
 
   // FileInputStream is finalizable and extends InputStream which isn't.
-  c = class_linker_->FindSystemClass("Ljava/io/InputStream;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/InputStream;");
   EXPECT_FALSE(c->IsFinalizable());
-  c = class_linker_->FindSystemClass("Ljava/io/FileInputStream;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/FileInputStream;");
   EXPECT_TRUE(c->IsFinalizable());
 
   // ScheduledThreadPoolExecutor doesn't have a finalize method but
   // extends ThreadPoolExecutor which does.
-  c = class_linker_->FindSystemClass("Ljava/util/concurrent/ThreadPoolExecutor;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/concurrent/ThreadPoolExecutor;");
   EXPECT_TRUE(c->IsFinalizable());
-  c = class_linker_->FindSystemClass("Ljava/util/concurrent/ScheduledThreadPoolExecutor;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/concurrent/ScheduledThreadPoolExecutor;");
   EXPECT_TRUE(c->IsFinalizable());
 }
 
diff --git a/runtime/common_test.h b/runtime/common_test.h
index 7f9b6b1..9eaec46 100644
--- a/runtime/common_test.h
+++ b/runtime/common_test.h
@@ -28,7 +28,9 @@
 #include "../compiler/compiler_backend.h"
 #include "../compiler/dex/quick/dex_file_to_method_inliner_map.h"
 #include "../compiler/dex/verification_results.h"
+#include "../compiler/driver/compiler_callbacks_impl.h"
 #include "../compiler/driver/compiler_driver.h"
+#include "../compiler/driver/compiler_options.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
 #include "base/stringprintf.h"
@@ -418,8 +420,9 @@
   void MakeExecutable(mirror::ClassLoader* class_loader, const char* class_name)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     std::string class_descriptor(DotToDescriptor(class_name));
-    SirtRef<mirror::ClassLoader> loader(Thread::Current(), class_loader);
-    mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), loader);
+    Thread* self = Thread::Current();
+    SirtRef<mirror::ClassLoader> loader(self, class_loader);
+    mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader);
     CHECK(klass != NULL) << "Class not found " << class_name;
     for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
       MakeExecutable(klass->GetDirectMethod(i));
@@ -458,11 +461,13 @@
         ? CompilerBackend::kPortable
         : CompilerBackend::kQuick;
 
-    verification_results_.reset(new VerificationResults);
+    compiler_options_.reset(new CompilerOptions);
+    verification_results_.reset(new VerificationResults(compiler_options_.get()));
     method_inliner_map_.reset(new DexFileToMethodInlinerMap);
-    callbacks_.Reset(verification_results_.get(), method_inliner_map_.get());
+    callbacks_.reset(new CompilerCallbacksImpl(verification_results_.get(),
+                                               method_inliner_map_.get()));
     Runtime::Options options;
-    options.push_back(std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
+    options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
     options.push_back(std::make_pair("bootclasspath", &boot_class_path_));
     options.push_back(std::make_pair("-Xcheck:jni", reinterpret_cast<void*>(NULL)));
     options.push_back(std::make_pair(min_heap_string.c_str(), reinterpret_cast<void*>(NULL)));
@@ -472,8 +477,8 @@
       return;
     }
     runtime_.reset(Runtime::Current());
-    // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
-    // give it away now and then switch to a more managable ScopedObjectAccess.
+    // Runtime::Create acquired the mutator_lock_ that is normally given away when we
+    // Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess.
     Thread::Current()->TransitionFromRunnableToSuspended(kNative);
     {
       ScopedObjectAccess soa(Thread::Current());
@@ -512,7 +517,8 @@
       }
       class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
       timer_.reset(new CumulativeLogger("Compilation times"));
-      compiler_driver_.reset(new CompilerDriver(verification_results_.get(),
+      compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+                                                verification_results_.get(),
                                                 method_inliner_map_.get(),
                                                 compiler_backend, instruction_set,
                                                 instruction_set_features,
@@ -563,9 +569,10 @@
 
     compiler_driver_.reset();
     timer_.reset();
-    callbacks_.Reset(nullptr, nullptr);
+    callbacks_.reset();
     method_inliner_map_.reset();
     verification_results_.reset();
+    compiler_options_.reset();
     STLDeleteElements(&opened_dex_files_);
 
     Runtime::Current()->GetHeap()->VerifyHeap();  // Check for heap corruption after the test
@@ -632,8 +639,9 @@
   void CompileClass(mirror::ClassLoader* class_loader, const char* class_name)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     std::string class_descriptor(DotToDescriptor(class_name));
-    SirtRef<mirror::ClassLoader> loader(Thread::Current(), class_loader);
-    mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), loader);
+    Thread* self = Thread::Current();
+    SirtRef<mirror::ClassLoader> loader(self, class_loader);
+    mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader);
     CHECK(klass != NULL) << "Class not found " << class_name;
     for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
       CompileMethod(klass->GetDirectMethod(i));
@@ -656,7 +664,8 @@
                            const char* method_name, const char* signature)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     std::string class_descriptor(DotToDescriptor(class_name));
-    mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader);
+    Thread* self = Thread::Current();
+    mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader);
     CHECK(klass != NULL) << "Class not found " << class_name;
     mirror::ArtMethod* method = klass->FindDirectMethod(method_name, signature);
     CHECK(method != NULL) << "Direct method not found: "
@@ -668,7 +677,8 @@
                             const char* method_name, const char* signature)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     std::string class_descriptor(DotToDescriptor(class_name));
-    mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader);
+    Thread* self = Thread::Current();
+    mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader);
     CHECK(klass != NULL) << "Class not found " << class_name;
     mirror::ArtMethod* method = klass->FindVirtualMethod(method_name, signature);
     CHECK(method != NULL) << "Virtual method not found: "
@@ -693,36 +703,6 @@
     image_reservation_.reset();
   }
 
-  class TestCompilerCallbacks : public CompilerCallbacks {
-   public:
-    TestCompilerCallbacks() : verification_results_(nullptr), method_inliner_map_(nullptr) {}
-
-    void Reset(VerificationResults* verification_results,
-               DexFileToMethodInlinerMap* method_inliner_map) {
-        verification_results_ = verification_results;
-        method_inliner_map_ = method_inliner_map;
-    }
-
-    virtual bool MethodVerified(verifier::MethodVerifier* verifier)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-      CHECK(verification_results_);
-      bool result = verification_results_->ProcessVerifiedMethod(verifier);
-      if (result && method_inliner_map_ != nullptr) {
-        MethodReference ref = verifier->GetMethodReference();
-        method_inliner_map_->GetMethodInliner(ref.dex_file)
-            ->AnalyseMethodCode(verifier);
-      }
-      return result;
-    }
-    virtual void ClassRejected(ClassReference ref) {
-      verification_results_->AddRejectedClass(ref);
-    }
-
-   private:
-    VerificationResults* verification_results_;
-    DexFileToMethodInlinerMap* method_inliner_map_;
-  };
-
   std::string android_data_;
   std::string dalvik_cache_;
   const DexFile* java_lang_dex_file_;  // owned by runtime_
@@ -730,9 +710,10 @@
   UniquePtr<Runtime> runtime_;
   // Owned by the runtime
   ClassLinker* class_linker_;
+  UniquePtr<CompilerOptions> compiler_options_;
   UniquePtr<VerificationResults> verification_results_;
   UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
-  TestCompilerCallbacks callbacks_;
+  UniquePtr<CompilerCallbacksImpl> callbacks_;
   UniquePtr<CompilerDriver> compiler_driver_;
   UniquePtr<CumulativeLogger> timer_;
 
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 89f841e..d3f684d 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -289,27 +289,39 @@
   return static_cast<JDWP::JdwpTag>(descriptor[0]);
 }
 
-static JDWP::JdwpTag TagFromClass(mirror::Class* c)
+static JDWP::JdwpTag TagFromClass(const ScopedObjectAccessUnchecked& soa, mirror::Class* c)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   CHECK(c != NULL);
   if (c->IsArrayClass()) {
     return JDWP::JT_ARRAY;
   }
-
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   if (c->IsStringClass()) {
     return JDWP::JT_STRING;
-  } else if (c->IsClassClass()) {
-    return JDWP::JT_CLASS_OBJECT;
-  } else if (class_linker->FindSystemClass("Ljava/lang/Thread;")->IsAssignableFrom(c)) {
-    return JDWP::JT_THREAD;
-  } else if (class_linker->FindSystemClass("Ljava/lang/ThreadGroup;")->IsAssignableFrom(c)) {
-    return JDWP::JT_THREAD_GROUP;
-  } else if (class_linker->FindSystemClass("Ljava/lang/ClassLoader;")->IsAssignableFrom(c)) {
-    return JDWP::JT_CLASS_LOADER;
-  } else {
-    return JDWP::JT_OBJECT;
   }
+  if (c->IsClassClass()) {
+    return JDWP::JT_CLASS_OBJECT;
+  }
+  {
+    mirror::Class* thread_class = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
+    if (thread_class->IsAssignableFrom(c)) {
+      return JDWP::JT_THREAD;
+    }
+  }
+  {
+    mirror::Class* thread_group_class =
+        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+    if (thread_group_class->IsAssignableFrom(c)) {
+      return JDWP::JT_THREAD_GROUP;
+    }
+  }
+  {
+    mirror::Class* class_loader_class =
+        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ClassLoader);
+    if (class_loader_class->IsAssignableFrom(c)) {
+      return JDWP::JT_CLASS_LOADER;
+    }
+  }
+  return JDWP::JT_OBJECT;
 }
 
 /*
@@ -320,9 +332,9 @@
  *
  * Null objects are tagged JT_OBJECT.
  */
-static JDWP::JdwpTag TagFromObject(mirror::Object* o)
+static JDWP::JdwpTag TagFromObject(const ScopedObjectAccessUnchecked& soa, mirror::Object* o)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
+  return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(soa, o->GetClass());
 }
 
 static bool IsPrimitiveTag(JDWP::JdwpTag tag) {
@@ -1011,11 +1023,12 @@
 }
 
 JDWP::JdwpError Dbg::GetObjectTag(JDWP::ObjectId object_id, uint8_t& tag) {
+  ScopedObjectAccessUnchecked soa(Thread::Current());
   mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
   if (o == ObjectRegistry::kInvalidObject) {
     return JDWP::ERR_INVALID_OBJECT;
   }
-  tag = TagFromObject(o);
+  tag = TagFromObject(soa, o);
   return JDWP::ERR_NONE;
 }
 
@@ -1062,7 +1075,7 @@
 JDWP::JdwpError Dbg::OutputArray(JDWP::ObjectId array_id, int offset, int count, JDWP::ExpandBuf* pReply) {
   JDWP::JdwpError status;
   mirror::Array* a = DecodeArray(array_id, status);
-  if (a == NULL) {
+  if (a == nullptr) {
     return status;
   }
 
@@ -1093,10 +1106,12 @@
       memcpy(dst, &src[offset * width], count * width);
     }
   } else {
+    ScopedObjectAccessUnchecked soa(Thread::Current());
     mirror::ObjectArray<mirror::Object>* oa = a->AsObjectArray<mirror::Object>();
     for (int i = 0; i < count; ++i) {
       mirror::Object* element = oa->Get(offset + i);
-      JDWP::JdwpTag specific_tag = (element != NULL) ? TagFromObject(element) : tag;
+      JDWP::JdwpTag specific_tag = (element != nullptr) ? TagFromObject(soa, element)
+                                                        : tag;
       expandBufAdd1(pReply, specific_tag);
       expandBufAddObjectId(pReply, gRegistry->Add(element));
     }
@@ -1639,8 +1654,9 @@
       CHECK_EQ(tag, JDWP::JT_VOID);
     }
   } else {
+    ScopedObjectAccessUnchecked soa(Thread::Current());
     mirror::Object* value = return_value->GetL();
-    expandBufAdd1(pReply, TagFromObject(value));
+    expandBufAdd1(pReply, TagFromObject(soa, value));
     expandBufAddObjectId(pReply, gRegistry->Add(value));
   }
 }
@@ -1672,7 +1688,7 @@
   if (thread_object == ObjectRegistry::kInvalidObject) {
     return JDWP::ERR_INVALID_OBJECT;
   }
-
+  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroup");
   // Okay, so it's an object, but is it actually a thread?
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread;
@@ -1685,14 +1701,14 @@
   if (error != JDWP::ERR_NONE) {
     return error;
   }
-
-  mirror::Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Thread;");
-  CHECK(c != NULL);
+  mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
+  CHECK(c != nullptr);
   mirror::ArtField* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
   CHECK(f != NULL);
   mirror::Object* group = f->GetObject(thread_object);
   CHECK(group != NULL);
   JDWP::ObjectId thread_group_id = gRegistry->Add(group);
+  soa.Self()->EndAssertNoThreadSuspension(old_cause);
 
   expandBufAddObjectId(pReply, thread_group_id);
   return JDWP::ERR_NONE;
@@ -1701,25 +1717,28 @@
 std::string Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id) {
   ScopedObjectAccess soa(Thread::Current());
   mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
-  CHECK(thread_group != NULL);
-
-  mirror::Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
-  CHECK(c != NULL);
+  CHECK(thread_group != nullptr);
+  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupName");
+  mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+  CHECK(c != nullptr);
   mirror::ArtField* f = c->FindInstanceField("name", "Ljava/lang/String;");
   CHECK(f != NULL);
   mirror::String* s = reinterpret_cast<mirror::String*>(f->GetObject(thread_group));
+  soa.Self()->EndAssertNoThreadSuspension(old_cause);
   return s->ToModifiedUtf8();
 }
 
 JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id) {
+  ScopedObjectAccessUnchecked soa(Thread::Current());
   mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
-  CHECK(thread_group != NULL);
-
-  mirror::Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
-  CHECK(c != NULL);
+  CHECK(thread_group != nullptr);
+  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupParent");
+  mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+  CHECK(c != nullptr);
   mirror::ArtField* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
   CHECK(f != NULL);
   mirror::Object* parent = f->GetObject(thread_group);
+  soa.Self()->EndAssertNoThreadSuspension(old_cause);
   return gRegistry->Add(parent);
 }
 
@@ -2090,13 +2109,13 @@
   return JDWP::ERR_NONE;
 }
 
-void Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag,
-                        uint8_t* buf, size_t width) {
+void Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
+                        JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
   struct GetLocalVisitor : public StackVisitor {
-    GetLocalVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id, int slot,
-                    JDWP::JdwpTag tag, uint8_t* buf, size_t width)
+    GetLocalVisitor(const ScopedObjectAccessUnchecked& soa, Thread* thread, Context* context,
+                    JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, context), frame_id_(frame_id), slot_(slot), tag_(tag),
+        : StackVisitor(thread, context), soa_(soa), frame_id_(frame_id), slot_(slot), tag_(tag),
           buf_(buf), width_(width) {}
 
     // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
@@ -2175,7 +2194,7 @@
           if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
             LOG(FATAL) << "Register " << reg << " expected to hold object: " << o;
           }
-          tag_ = TagFromObject(o);
+          tag_ = TagFromObject(soa_, o);
           JDWP::SetObjectId(buf_+1, gRegistry->Add(o));
         }
         break;
@@ -2208,7 +2227,7 @@
       JDWP::Set1(buf_, tag_);
       return false;
     }
-
+    const ScopedObjectAccessUnchecked& soa_;
     const JDWP::FrameId frame_id_;
     const int slot_;
     JDWP::JdwpTag tag_;
@@ -2224,7 +2243,7 @@
     return;
   }
   UniquePtr<Context> context(Context::Create());
-  GetLocalVisitor visitor(thread, context.get(), frame_id, slot, tag, buf, width);
+  GetLocalVisitor visitor(soa, thread, context.get(), frame_id, slot, tag, buf, width);
   visitor.WalkStack();
 }
 
@@ -3037,7 +3056,7 @@
     pReq->result_value.SetJ(0);
   } else if (pReq->result_tag == JDWP::JT_OBJECT) {
     /* if no exception thrown, examine object result more closely */
-    JDWP::JdwpTag new_tag = TagFromObject(pReq->result_value.GetL());
+    JDWP::JdwpTag new_tag = TagFromObject(soa, pReq->result_value.GetL());
     if (new_tag != pReq->result_tag) {
       VLOG(jdwp) << "  JDWP promoted result from " << pReq->result_tag << " to " << new_tag;
       pReq->result_tag = new_tag;
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8b94b5a..2c08351 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -18,7 +18,7 @@
 #define ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
 
 #include "base/macros.h"
-#include "class_linker.h"
+#include "class_linker-inl.h"
 #include "common_throws.h"
 #include "dex_file.h"
 #include "indirect_reference_table.h"
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index f7b621f..c7f537a 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -39,7 +39,7 @@
     ScopedObjectAccess soa(Thread::Current());
     SirtRef<mirror::ClassLoader> class_loader(
         soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("ExceptionHandle")));
-    my_klass_ = class_linker_->FindClass("LExceptionHandle;", class_loader);
+    my_klass_ = class_linker_->FindClass(soa.Self(), "LExceptionHandle;", class_loader);
     ASSERT_TRUE(my_klass_ != NULL);
     SirtRef<mirror::Class> sirt_klass(soa.Self(), my_klass_);
     class_linker_->EnsureInitialized(sirt_klass, true, true);
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index fb797e0..9ab2d2e 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -65,6 +65,7 @@
 constexpr bool kUseRecursiveMark = false;
 constexpr bool kUseMarkStackPrefetch = true;
 constexpr size_t kSweepArrayChunkFreeSize = 1024;
+constexpr bool kPreCleanCards = true;
 
 // Parallelism options.
 constexpr bool kParallelCardScan = true;
@@ -232,6 +233,25 @@
   return is_concurrent_;
 }
 
+void MarkSweep::PreCleanCards() {
+  // Don't do this for non concurrent GCs since they don't have any dirty cards.
+  if (kPreCleanCards && IsConcurrent()) {
+    Thread* self = Thread::Current();
+    CHECK(!Locks::mutator_lock_->IsExclusiveHeld(self));
+    // Process dirty cards and add dirty cards to mod union tables, also ages cards.
+    heap_->ProcessCards(timings_);
+    // Required so that we see aged cards before we start scanning the cards.
+    MarkThreadRoots(self);
+    // TODO: Only mark the dirty roots.
+    MarkNonThreadRoots();
+    MarkConcurrentRoots();
+    // Process the newly aged cards.
+    RecursiveMarkDirtyObjects(false, accounting::CardTable::kCardDirty - 1);
+    // TODO: Empty allocation stack to reduce the number of objects we need to test / mark as live
+    // in the next GC.
+  }
+}
+
 void MarkSweep::MarkingPhase() {
   TimingLogger::ScopedSplit split("MarkingPhase", &timings_);
   Thread* self = Thread::Current();
@@ -263,6 +283,8 @@
   MarkConcurrentRoots();
   UpdateAndMarkModUnion();
   MarkReachableObjects();
+  // Pre-clean dirtied cards to reduce pauses.
+  PreCleanCards();
 }
 
 void MarkSweep::UpdateAndMarkModUnion() {
@@ -313,20 +335,24 @@
     TimingLogger::ScopedSplit split("UnMarkAllocStack", &timings_);
     WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
     accounting::ObjectStack* allocation_stack = GetHeap()->allocation_stack_.get();
-    // The allocation stack contains things allocated since the start of the GC. These may have been
-    // marked during this GC meaning they won't be eligible for reclaiming in the next sticky GC.
-    // Remove these objects from the mark bitmaps so that they will be eligible for sticky
-    // collection.
-    // There is a race here which is safely handled. Another thread such as the hprof could
-    // have flushed the alloc stack after we resumed the threads. This is safe however, since
-    // reseting the allocation stack zeros it out with madvise. This means that we will either
-    // read NULLs or attempt to unmark a newly allocated object which will not be marked in the
-    // first place.
-    mirror::Object** end = allocation_stack->End();
-    for (mirror::Object** it = allocation_stack->Begin(); it != end; ++it) {
-      const Object* obj = *it;
-      if (obj != NULL) {
-        UnMarkObjectNonNull(obj);
+    if (!kPreCleanCards) {
+      // The allocation stack contains things allocated since the start of the GC. These may have
+      // been marked during this GC meaning they won't be eligible for reclaiming in the next
+      // sticky GC. Unmark these objects so that they are eligible for reclaiming in the next
+      // sticky GC.
+      // There is a race here which is safely handled. Another thread such as the hprof could
+      // have flushed the alloc stack after we resumed the threads. This is safe however, since
+      // reseting the allocation stack zeros it out with madvise. This means that we will either
+      // read NULLs or attempt to unmark a newly allocated object which will not be marked in the
+      // first place.
+      // We can't do this if we pre-clean cards since we will unmark objects which are no longer on
+      // a dirty card since we aged cards during the pre-cleaning process.
+      mirror::Object** end = allocation_stack->End();
+      for (mirror::Object** it = allocation_stack->Begin(); it != end; ++it) {
+        const Object* obj = *it;
+        if (obj != nullptr) {
+          UnMarkObjectNonNull(obj);
+        }
       }
     }
   }
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 963b9ea..8f8a0f0 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -135,6 +135,11 @@
   virtual void UpdateAndMarkModUnion()
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Pre clean cards to reduce how much work is needed in the pause.
+  void PreCleanCards()
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Sweeps unmarked objects to complete the garbage collection.
   virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc
index 9e3adb4..450445e 100644
--- a/runtime/gc/collector/sticky_mark_sweep.cc
+++ b/runtime/gc/collector/sticky_mark_sweep.cc
@@ -53,6 +53,8 @@
   // TODO: Not put these objects in the mark stack in the first place.
   mark_stack_->Reset();
   RecursiveMarkDirtyObjects(false, accounting::CardTable::kCardDirty - 1);
+  // Pre clean dirtied cards to reduce pauses.
+  PreCleanCards();
 }
 
 void StickyMarkSweep::Sweep(bool swap_bitmaps) {
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index b02b8bb..4b86339 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -43,7 +43,8 @@
     ScopedObjectAccess soa(Thread::Current());
     // garbage is created during ClassLinker::Init
 
-    SirtRef<mirror::Class> c(soa.Self(), class_linker_->FindSystemClass("[Ljava/lang/Object;"));
+    SirtRef<mirror::Class> c(soa.Self(), class_linker_->FindSystemClass(soa.Self(),
+                                                                        "[Ljava/lang/Object;"));
     for (size_t i = 0; i < 1024; ++i) {
       SirtRef<mirror::ObjectArray<mirror::Object> > array(soa.Self(),
           mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), c.get(), 2048));
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index ebad8dd..86dee1d 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -89,52 +89,14 @@
     arg_vector.push_back("--host");
   }
 
+  const std::vector<std::string>& compiler_options = Runtime::Current()->GetImageCompilerOptions();
+  for (size_t i = 0; compiler_options.size(); ++i) {
+    arg_vector.push_back(compiler_options[i].c_str());
+  }
+
   std::string command_line(Join(arg_vector, ' '));
   LOG(INFO) << "GenerateImage: " << command_line;
-
-  // Convert the args to char pointers.
-  std::vector<char*> char_args;
-  for (std::vector<std::string>::iterator it = arg_vector.begin(); it != arg_vector.end();
-      ++it) {
-    char_args.push_back(const_cast<char*>(it->c_str()));
-  }
-  char_args.push_back(NULL);
-
-  // fork and exec dex2oat
-  pid_t pid = fork();
-  if (pid == 0) {
-    // no allocation allowed between fork and exec
-
-    // change process groups, so we don't get reaped by ProcessManager
-    setpgid(0, 0);
-
-    execv(dex2oat.c_str(), &char_args[0]);
-
-    PLOG(FATAL) << "execv(" << dex2oat << ") failed";
-    return false;
-  } else {
-    if (pid == -1) {
-      *error_msg = StringPrintf("Failed to generate image '%s' because fork failed: %s",
-                                image_file_name.c_str(), strerror(errno));
-      return false;
-    }
-
-    // wait for dex2oat to finish
-    int status;
-    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-    if (got_pid != pid) {
-      *error_msg = StringPrintf("Failed to generate image '%s' because waitpid failed: "
-                                "wanted %d, got %d: %s",
-                                image_file_name.c_str(), pid, got_pid, strerror(errno));
-      return false;
-    }
-    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-      *error_msg = StringPrintf("Failed to generate image '%s' because dex2oat failed: %s",
-                                image_file_name.c_str(), command_line.c_str());
-      return false;
-    }
-  }
-  return true;
+  return Exec(arg_vector, error_msg);
 }
 
 ImageSpace* ImageSpace::Create(const char* original_image_file_name) {
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index d01bf2c..093967e 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -43,8 +43,9 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Note the minimum size, which is the size of a zero-length byte array.
     EXPECT_GE(size, SizeOfZeroLengthByteArray());
-    SirtRef<mirror::ClassLoader> null_loader(Thread::Current(), nullptr);
-    mirror::Class* byte_array_class = Runtime::Current()->GetClassLinker()->FindClass("[B",
+    Thread* self = Thread::Current();
+    SirtRef<mirror::ClassLoader> null_loader(self, nullptr);
+    mirror::Class* byte_array_class = Runtime::Current()->GetClassLinker()->FindClass(self, "[B",
                                                                                       null_loader);
     EXPECT_TRUE(byte_array_class != nullptr);
     o->SetClass(byte_array_class);
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index b6c6cb4..78e1992 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -48,7 +48,7 @@
   static const size_t kTableMax = 20;
   IndirectReferenceTable irt(kTableInitial, kTableMax, kGlobal);
 
-  mirror::Class* c = class_linker_->FindSystemClass("Ljava/lang/Object;");
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
   ASSERT_TRUE(c != NULL);
   mirror::Object* obj0 = c->AllocObject(soa.Self());
   ASSERT_TRUE(obj0 != NULL);
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 9e1d915..83a1fbc 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -280,7 +280,7 @@
     std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str()));
 
     SirtRef<ClassLoader> class_loader(self, nullptr);  // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader();
-    Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
+    Class* found = Runtime::Current()->GetClassLinker()->FindClass(self, descriptor.c_str(),
                                                                    class_loader);
     CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: "
         << PrettyDescriptor(descriptor);
@@ -289,7 +289,7 @@
     SirtRef<ClassLoader> class_loader(self, down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset)));
     std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset + 1)->AsString()->ToModifiedUtf8().c_str()));
 
-    Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
+    Class* found = Runtime::Current()->GetClassLinker()->FindClass(self, descriptor.c_str(),
                                                                    class_loader);
     result->SetL(found);
   } else if (name == "java.lang.Object java.lang.Class.newInstance()") {
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index e45cb6e..1ec795f 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -154,8 +154,9 @@
    * release it in the "clear" call.
    */
   // ObjectId GetWaitForEventThread();
-  void SetWaitForEventThread(ObjectId threadId);
-  void ClearWaitForEventThread();
+  void SetWaitForEventThread(ObjectId threadId)
+      LOCKS_EXCLUDED(event_thread_lock_, process_request_lock_);
+  void ClearWaitForEventThread() LOCKS_EXCLUDED(event_thread_lock);
 
   /*
    * These notify the debug code that something interesting has happened.  This
@@ -346,7 +347,7 @@
 
   // Used to synchronize request processing and event sending (to avoid sending an event before
   // sending the reply of a command being processed).
-  Mutex process_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  Mutex process_request_lock_ ACQUIRED_AFTER(event_thread_lock_);
   ConditionVariable process_request_cond_ GUARDED_BY(process_request_lock_);
   bool processing_request_ GUARDED_BY(process_request_lock_);
 
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 0ff78d0..4b170ba 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -1685,6 +1685,12 @@
   SetWaitForEventThread(0);
 
   /*
+   * We do not want events to be sent while we process a request. Indicate the JDWP thread starts
+   * to process a request so other threads wait for it to finish before sending an event.
+   */
+  StartProcessingRequest();
+
+  /*
    * Tell the VM that we're running and shouldn't be interrupted by GC.
    * Do this after anything that can stall indefinitely.
    */
@@ -1779,9 +1785,15 @@
   Thread* self = Thread::Current();
   CHECK_NE(self, GetDebugThread()) << "Events should not be posted by debug thread";
   MutexLock mu(self, process_request_lock_);
+  bool waited = false;
   while (processing_request_) {
+    VLOG(jdwp) << StringPrintf("wait for processing request");
+    waited = true;
     process_request_cond_.Wait(self);
   }
+  if (waited) {
+    VLOG(jdwp) << StringPrintf("finished waiting for processing request");
+  }
   CHECK_EQ(processing_request_, false);
 }
 
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index ba49c45..500585d 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -386,10 +386,14 @@
   JdwpNetStateBase* netStateBase = reinterpret_cast<JdwpNetStateBase*>(netState);
   JDWP::Request request(netStateBase->input_buffer_, netStateBase->input_count_);
 
-  StartProcessingRequest();
   ExpandBuf* pReply = expandBufAlloc();
   ProcessRequest(request, pReply);
   ssize_t cc = netStateBase->WritePacket(pReply);
+
+  /*
+   * We processed this request and sent its reply. Notify other threads waiting for us they can now
+   * send events.
+   */
   EndProcessingRequest();
 
   if (cc != (ssize_t) expandBufGetLength(pReply)) {
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index a0665b5..76aa734 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -27,7 +27,7 @@
 #include "base/mutex.h"
 #include "base/stl_util.h"
 #include "base/stringpiece.h"
-#include "class_linker.h"
+#include "class_linker-inl.h"
 #include "dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "interpreter/interpreter.h"
@@ -284,7 +284,7 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   if (sig[1] != '\0') {
     SirtRef<mirror::ClassLoader> class_loader(soa.Self(), c->GetClassLoader());
-    field_type = class_linker->FindClass(sig, class_loader);
+    field_type = class_linker->FindClass(soa.Self(), sig, class_loader);
   } else {
     field_type = class_linker->FindPrimitiveClass(*sig);
   }
@@ -651,9 +651,9 @@
     mirror::Class* c = nullptr;
     if (runtime->IsStarted()) {
       SirtRef<mirror::ClassLoader> class_loader(soa.Self(), GetClassLoader(soa));
-      c = class_linker->FindClass(descriptor.c_str(), class_loader);
+      c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader);
     } else {
-      c = class_linker->FindSystemClass(descriptor.c_str());
+      c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str());
     }
     return soa.AddLocalReference<jclass>(c);
   }
@@ -2140,13 +2140,8 @@
                   PrettyDescriptor(element_class).c_str());
         return nullptr;
       }
-      std::string descriptor("[");
-      descriptor += ClassHelper(element_class).GetDescriptor();
-
-      // Find the class.
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-      SirtRef<mirror::ClassLoader> class_loader(soa.Self(), element_class->GetClassLoader());
-      array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+      array_class = class_linker->FindArrayClass(soa.Self(), element_class);
       if (UNLIKELY(array_class == nullptr)) {
         return nullptr;
       }
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 2dd7d96..63bc45c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -107,7 +107,8 @@
                      class_name);
     }
 
-    mirror::Class* c = class_linker_->FindClass(DotToDescriptor(class_name).c_str(), class_loader);
+    mirror::Class* c = class_linker_->FindClass(self, DotToDescriptor(class_name).c_str(),
+                                                class_loader);
     CHECK(c != NULL);
 
     *method = is_static ? c->FindDirectMethod(method_name, method_signature)
@@ -1778,7 +1779,7 @@
       class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
   CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V");
 
-  mirror::Class* klass = class_linker_->FindClass("LMain;", class_loader);
+  mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
   ASSERT_TRUE(klass != NULL);
 
   mirror::ArtMethod* method = klass->FindDirectMethod("main", "([Ljava/lang/String;)V");
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 2180857..715f072 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -18,6 +18,7 @@
 
 #include "class.h"
 #include "class-inl.h"
+#include "class_linker-inl.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
@@ -85,20 +86,22 @@
     }
   }
 
-  // Generate the full name of the array class.
-  std::string descriptor(num_dimensions, '[');
-  descriptor += ClassHelper(element_class.get()).GetDescriptor();
-
   // Find/generate the array class.
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  SirtRef<mirror::ClassLoader> class_loader(self, element_class->GetClassLoader());
   SirtRef<mirror::Class> array_class(self,
-                                     class_linker->FindClass(descriptor.c_str(), class_loader));
+                                     class_linker->FindArrayClass(self, element_class.get()));
   if (UNLIKELY(array_class.get() == nullptr)) {
     CHECK(self->IsExceptionPending());
     return nullptr;
   }
-  // create the array
+  for (int32_t i = 1; i < dimensions->GetLength(); ++i) {
+    array_class.reset(class_linker->FindArrayClass(self, array_class.get()));
+    if (UNLIKELY(array_class.get() == nullptr)) {
+      CHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+  }
+  // Create the array.
   Array* new_array = RecursiveCreateMultiArray(self, array_class, 0, dimensions);
   if (UNLIKELY(new_array == nullptr)) {
     CHECK(self->IsExceptionPending());
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 3208de9..6dbb29d 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -72,7 +72,7 @@
             << PrettyClass(this) << " " << old_status << " -> " << new_status;
     }
   }
-  if (new_status == kStatusError) {
+  if (UNLIKELY(new_status == kStatusError)) {
     CHECK_NE(GetStatus(), kStatusError)
         << "Attempt to set as erroneous an already erroneous class " << PrettyClass(this);
 
@@ -95,7 +95,8 @@
     // clear exception to call FindSystemClass
     self->ClearException();
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Class* eiie_class = class_linker->FindSystemClass("Ljava/lang/ExceptionInInitializerError;");
+    Class* eiie_class = class_linker->FindSystemClass(self,
+                                                      "Ljava/lang/ExceptionInInitializerError;");
     CHECK(!self->IsExceptionPending());
 
     // Only verification errors, not initialization problems, should set a verify error.
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 40c3748..34fb15e 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -123,7 +123,8 @@
   EXPECT_TRUE(oa->Get(0) == oa.get());
   EXPECT_TRUE(oa->Get(1) == oa.get());
 
-  Class* aioobe = class_linker_->FindSystemClass("Ljava/lang/ArrayIndexOutOfBoundsException;");
+  Class* aioobe = class_linker_->FindSystemClass(soa.Self(),
+                                                 "Ljava/lang/ArrayIndexOutOfBoundsException;");
 
   EXPECT_TRUE(oa->Get(-1) == NULL);
   EXPECT_TRUE(soa.Self()->IsExceptionPending());
@@ -138,21 +139,23 @@
   ASSERT_TRUE(oa->GetClass() != NULL);
   ClassHelper oa_ch(oa->GetClass());
   ASSERT_EQ(2U, oa_ch.NumDirectInterfaces());
-  EXPECT_EQ(class_linker_->FindSystemClass("Ljava/lang/Cloneable;"), oa_ch.GetDirectInterface(0));
-  EXPECT_EQ(class_linker_->FindSystemClass("Ljava/io/Serializable;"), oa_ch.GetDirectInterface(1));
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;"),
+            oa_ch.GetDirectInterface(0));
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;"),
+            oa_ch.GetDirectInterface(1));
 }
 
 TEST_F(ObjectTest, AllocArray) {
   ScopedObjectAccess soa(Thread::Current());
-  Class* c = class_linker_->FindSystemClass("[I");
+  Class* c = class_linker_->FindSystemClass(soa.Self(), "[I");
   SirtRef<Array> a(soa.Self(), Array::Alloc<true>(soa.Self(), c, 1));
   ASSERT_TRUE(c == a->GetClass());
 
-  c = class_linker_->FindSystemClass("[Ljava/lang/Object;");
+  c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
   a.reset(Array::Alloc<true>(soa.Self(), c, 1));
   ASSERT_TRUE(c == a->GetClass());
 
-  c = class_linker_->FindSystemClass("[[Ljava/lang/Object;");
+  c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
   a.reset(Array::Alloc<true>(soa.Self(), c, 1));
   ASSERT_TRUE(c == a->GetClass());
 }
@@ -173,7 +176,7 @@
   EXPECT_EQ(T(123), a->Get(0));
   EXPECT_EQ(T(321), a->Get(1));
 
-  Class* aioobe = cl->FindSystemClass("Ljava/lang/ArrayIndexOutOfBoundsException;");
+  Class* aioobe = cl->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;");
 
   EXPECT_EQ(0, a->Get(-1));
   EXPECT_TRUE(soa.Self()->IsExceptionPending());
@@ -214,7 +217,7 @@
 TEST_F(ObjectTest, CheckAndAllocArrayFromCode) {
   // pretend we are trying to call 'new char[3]' from String.toCharArray
   ScopedObjectAccess soa(Thread::Current());
-  Class* java_util_Arrays = class_linker_->FindSystemClass("Ljava/util/Arrays;");
+  Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;");
   ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V");
   const DexFile::StringId* string_id = java_lang_dex_file_->FindStringId("[I");
   ASSERT_TRUE(string_id != NULL);
@@ -233,11 +236,11 @@
 TEST_F(ObjectTest, CreateMultiArray) {
   ScopedObjectAccess soa(Thread::Current());
 
-  SirtRef<Class> c(soa.Self(), class_linker_->FindSystemClass("I"));
+  SirtRef<Class> c(soa.Self(), class_linker_->FindSystemClass(soa.Self(), "I"));
   SirtRef<IntArray> dims(soa.Self(), IntArray::Alloc(soa.Self(), 1));
   dims->Set<false>(0, 1);
   Array* multi = Array::CreateMultiArray(soa.Self(), c, dims);
-  EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[I"));
+  EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I"));
   EXPECT_EQ(1, multi->GetLength());
 
   dims->Set<false>(0, -1);
@@ -253,11 +256,11 @@
       dims->Set<false>(0, i);
       dims->Set<false>(1, j);
       multi = Array::CreateMultiArray(soa.Self(), c, dims);
-      EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[[I"));
+      EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[[I"));
       EXPECT_EQ(i, multi->GetLength());
       for (int k = 0; k < i; ++k) {
         Array* outer = multi->AsObjectArray<Array>()->Get(k);
-        EXPECT_TRUE(outer->GetClass() == class_linker_->FindSystemClass("[I"));
+        EXPECT_TRUE(outer->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I"));
         EXPECT_EQ(j, outer->GetLength());
       }
     }
@@ -272,8 +275,7 @@
   CHECK(dex_file != NULL);
 
   SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<ClassLoader*>(class_loader));
-  Class* klass =
-      class_linker_->FindClass("LStaticsFromCode;", loader);
+  Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", loader);
   ArtMethod* clinit = klass->FindClassInitializer();
   const DexFile::StringId* klass_string_id = dex_file->FindStringId("LStaticsFromCode;");
   ASSERT_TRUE(klass_string_id != NULL);
@@ -404,9 +406,9 @@
   SirtRef<ClassLoader> class_loader_1(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader_1));
   SirtRef<ClassLoader> class_loader_2(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader_2));
 
-  Class* klass1 = linker->FindClass("LProtoCompare;", class_loader_1);
+  Class* klass1 = linker->FindClass(soa.Self(), "LProtoCompare;", class_loader_1);
   ASSERT_TRUE(klass1 != NULL);
-  Class* klass2 = linker->FindClass("LProtoCompare2;", class_loader_2);
+  Class* klass2 = linker->FindClass(soa.Self(), "LProtoCompare2;", class_loader_2);
   ASSERT_TRUE(klass2 != NULL);
 
   ArtMethod* m1_1 = klass1->GetVirtualMethod(0);
@@ -472,8 +474,8 @@
   jobject jclass_loader = LoadDex("XandY");
   SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader));
 
-  Class* X = class_linker_->FindClass("LX;", class_loader);
-  Class* Y = class_linker_->FindClass("LY;", class_loader);
+  Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
+  Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
   ASSERT_TRUE(X != NULL);
   ASSERT_TRUE(Y != NULL);
 
@@ -487,16 +489,16 @@
   EXPECT_TRUE(y->InstanceOf(X));
   EXPECT_TRUE(y->InstanceOf(Y));
 
-  Class* java_lang_Class = class_linker_->FindSystemClass("Ljava/lang/Class;");
-  Class* Object_array_class = class_linker_->FindSystemClass("[Ljava/lang/Object;");
+  Class* java_lang_Class = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Class;");
+  Class* Object_array_class = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
 
   EXPECT_FALSE(java_lang_Class->InstanceOf(Object_array_class));
   EXPECT_TRUE(Object_array_class->InstanceOf(java_lang_Class));
 
   // All array classes implement Cloneable and Serializable.
   Object* array = ObjectArray<Object>::Alloc(soa.Self(), Object_array_class, 1);
-  Class* java_lang_Cloneable = class_linker_->FindSystemClass("Ljava/lang/Cloneable;");
-  Class* java_io_Serializable = class_linker_->FindSystemClass("Ljava/io/Serializable;");
+  Class* java_lang_Cloneable = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;");
+  Class* java_io_Serializable = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;");
   EXPECT_TRUE(array->InstanceOf(java_lang_Cloneable));
   EXPECT_TRUE(array->InstanceOf(java_io_Serializable));
 }
@@ -505,8 +507,8 @@
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader = LoadDex("XandY");
   SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader));
-  Class* X = class_linker_->FindClass("LX;", class_loader);
-  Class* Y = class_linker_->FindClass("LY;", class_loader);
+  Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
+  Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
 
   EXPECT_TRUE(X->IsAssignableFrom(X));
   EXPECT_TRUE(X->IsAssignableFrom(Y));
@@ -514,8 +516,8 @@
   EXPECT_TRUE(Y->IsAssignableFrom(Y));
 
   // class final String implements CharSequence, ..
-  Class* string = class_linker_->FindSystemClass("Ljava/lang/String;");
-  Class* charseq = class_linker_->FindSystemClass("Ljava/lang/CharSequence;");
+  Class* string = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;");
+  Class* charseq = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/CharSequence;");
   // Can String be assigned to CharSequence without a cast?
   EXPECT_TRUE(charseq->IsAssignableFrom(string));
   // Can CharSequence be assigned to String without a cast?
@@ -542,36 +544,36 @@
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader = LoadDex("XandY");
   SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader));
-  Class* X = class_linker_->FindClass("LX;", class_loader);
-  Class* Y = class_linker_->FindClass("LY;", class_loader);
+  Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
+  Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
   ASSERT_TRUE(X != NULL);
   ASSERT_TRUE(Y != NULL);
 
-  Class* YA = class_linker_->FindClass("[LY;", class_loader);
-  Class* YAA = class_linker_->FindClass("[[LY;", class_loader);
+  Class* YA = class_linker_->FindClass(soa.Self(), "[LY;", class_loader);
+  Class* YAA = class_linker_->FindClass(soa.Self(), "[[LY;", class_loader);
   ASSERT_TRUE(YA != NULL);
   ASSERT_TRUE(YAA != NULL);
 
-  Class* XAA = class_linker_->FindClass("[[LX;", class_loader);
+  Class* XAA = class_linker_->FindClass(soa.Self(), "[[LX;", class_loader);
   ASSERT_TRUE(XAA != NULL);
 
-  Class* O = class_linker_->FindSystemClass("Ljava/lang/Object;");
-  Class* OA = class_linker_->FindSystemClass("[Ljava/lang/Object;");
-  Class* OAA = class_linker_->FindSystemClass("[[Ljava/lang/Object;");
-  Class* OAAA = class_linker_->FindSystemClass("[[[Ljava/lang/Object;");
+  Class* O = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
+  Class* OA = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
+  Class* OAA = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
+  Class* OAAA = class_linker_->FindSystemClass(soa.Self(), "[[[Ljava/lang/Object;");
   ASSERT_TRUE(O != NULL);
   ASSERT_TRUE(OA != NULL);
   ASSERT_TRUE(OAA != NULL);
   ASSERT_TRUE(OAAA != NULL);
 
-  Class* S = class_linker_->FindSystemClass("Ljava/io/Serializable;");
-  Class* SA = class_linker_->FindSystemClass("[Ljava/io/Serializable;");
-  Class* SAA = class_linker_->FindSystemClass("[[Ljava/io/Serializable;");
+  Class* S = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;");
+  Class* SA = class_linker_->FindSystemClass(soa.Self(), "[Ljava/io/Serializable;");
+  Class* SAA = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/io/Serializable;");
   ASSERT_TRUE(S != NULL);
   ASSERT_TRUE(SA != NULL);
   ASSERT_TRUE(SAA != NULL);
 
-  Class* IA = class_linker_->FindSystemClass("[I");
+  Class* IA = class_linker_->FindSystemClass(soa.Self(), "[I");
   ASSERT_TRUE(IA != NULL);
 
   EXPECT_TRUE(YAA->IsAssignableFrom(YAA));  // identity
@@ -616,7 +618,7 @@
   // TODO: check that s.count == 3.
 
   // Ensure that we handle superclass fields correctly...
-  c = class_linker_->FindSystemClass("Ljava/lang/StringBuilder;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/StringBuilder;");
   ASSERT_TRUE(c != NULL);
   // No StringBuilder.count...
   EXPECT_TRUE(c->FindDeclaredInstanceField("count", "I") == NULL);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index def3292..5779442 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -16,7 +16,7 @@
 
 #include <limits.h>
 
-#include "class_linker.h"
+#include "class_linker-inl.h"
 #include "common_throws.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
@@ -57,22 +57,22 @@
                                             jint length) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
-  if (element_class == NULL) {
+  if (UNLIKELY(element_class == nullptr)) {
     ThrowNullPointerException(NULL, "element class == null");
-    return NULL;
+    return nullptr;
   }
-  if (length < 0) {
+  if (UNLIKELY(length < 0)) {
     ThrowNegativeArraySizeException(length);
-    return NULL;
+    return nullptr;
   }
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  std::string descriptor;
-  descriptor += "[";
-  descriptor += ClassHelper(element_class).GetDescriptor();
-  SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr);
-  mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+  Runtime* runtime = Runtime::Current();
+  mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class);
+  if (UNLIKELY(array_class == nullptr)) {
+    return nullptr;
+  }
+  gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentNonMovingAllocator();
   mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
-                                                     Runtime::Current()->GetHeap()->GetCurrentNonMovingAllocator());
+                                                     allocator);
   return soa.AddLocalReference<jobject>(result);
 }
 
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 3e3f608..8bf36e7 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -43,7 +43,8 @@
 }
 
 // "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
-static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize, jobject javaLoader) {
+static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize,
+                                 jobject javaLoader) {
   ScopedObjectAccess soa(env);
   ScopedUtfChars name(env, javaName);
   if (name.c_str() == nullptr) {
@@ -64,7 +65,8 @@
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
                                             soa.Decode<mirror::ClassLoader*>(javaLoader));
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  SirtRef<mirror::Class> c(soa.Self(), class_linker->FindClass(descriptor.c_str(), class_loader));
+  SirtRef<mirror::Class> c(soa.Self(), class_linker->FindClass(soa.Self(), descriptor.c_str(),
+                                                               class_loader));
   if (c.get() == nullptr) {
     ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
     env->ExceptionClear();
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 2197597..fc30aa6 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "class_linker.h"
+#include "class_linker-inl.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
 #include "jni_internal.h"
@@ -50,12 +50,8 @@
     ThrowNegativeArraySizeException(length);
     return NULL;
   }
-  std::string descriptor("[");
-  descriptor += ClassHelper(element_class).GetDescriptor();
-
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  SirtRef<mirror::ClassLoader> class_loader(soa.Self(), element_class->GetClassLoader());
-  mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+  mirror::Class* array_class = class_linker->FindArrayClass(soa.Self(), element_class);
   if (UNLIKELY(array_class == NULL)) {
     CHECK(soa.Self()->IsExceptionPending());
     return NULL;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 00a8506..61f023c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -84,6 +84,7 @@
   // This won't work for portable runtime execution because it doesn't process relocations.
   UniquePtr<File> file(OS::OpenFileForReading(filename.c_str()));
   if (file.get() == NULL) {
+    *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
     return NULL;
   }
   return OpenElfFile(file.get(), location, requested_base, false, executable, error_msg);
diff --git a/runtime/object_utils.h b/runtime/object_utils.h
index a981fab..4eac291 100644
--- a/runtime/object_utils.h
+++ b/runtime/object_utils.h
@@ -17,7 +17,7 @@
 #ifndef ART_RUNTIME_OBJECT_UTILS_H_
 #define ART_RUNTIME_OBJECT_UTILS_H_
 
-#include "class_linker-inl.h"
+#include "class_linker.h"
 #include "dex_file.h"
 #include "monitor.h"
 #include "mirror/art_field.h"
@@ -158,10 +158,10 @@
     DCHECK(!klass_->IsPrimitive());
     if (klass_->IsArrayClass()) {
       if (idx == 0) {
-        return GetClassLinker()->FindSystemClass("Ljava/lang/Cloneable;");
+        return GetClassLinker()->FindSystemClass(Thread::Current(), "Ljava/lang/Cloneable;");
       } else {
         DCHECK_EQ(1U, idx);
-        return GetClassLinker()->FindSystemClass("Ljava/io/Serializable;");
+        return GetClassLinker()->FindSystemClass(Thread::Current(), "Ljava/io/Serializable;");
       }
     } else if (klass_->IsProxyClass()) {
       return klass_->GetIfTable()->GetInterface(idx);
@@ -251,7 +251,7 @@
   mirror::Class* GetType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     uint32_t field_index = field_->GetDexFieldIndex();
     if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
-      return GetClassLinker()->FindSystemClass(GetTypeDescriptor());
+      return GetClassLinker()->FindSystemClass(Thread::Current(), GetTypeDescriptor());
     }
     const DexFile& dex_file = GetDexFile();
     const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 3ccea36..1ef15f7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -77,13 +77,6 @@
       is_zygote_(false),
       is_concurrent_gc_enabled_(true),
       is_explicit_gc_disabled_(false),
-      compiler_filter_(kSpeed),
-      huge_method_threshold_(0),
-      large_method_threshold_(0),
-      small_method_threshold_(0),
-      tiny_method_threshold_(0),
-      num_dex_methods_threshold_(0),
-      sea_ir_mode_(false),
       default_stack_size_(0),
       heap_(nullptr),
       max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
@@ -452,14 +445,6 @@
   parsed->hook_exit_ = exit;
   parsed->hook_abort_ = NULL;  // We don't call abort(3) by default; see Runtime::Abort.
 
-  parsed->compiler_filter_ = Runtime::kDefaultCompilerFilter;
-  parsed->huge_method_threshold_ = Runtime::kDefaultHugeMethodThreshold;
-  parsed->large_method_threshold_ = Runtime::kDefaultLargeMethodThreshold;
-  parsed->small_method_threshold_ = Runtime::kDefaultSmallMethodThreshold;
-  parsed->tiny_method_threshold_ = Runtime::kDefaultTinyMethodThreshold;
-  parsed->num_dex_methods_threshold_ = Runtime::kDefaultNumDexMethodsThreshold;
-
-  parsed->sea_ir_mode_ = false;
 //  gLogVerbosity.class_linker = true;  // TODO: don't check this in!
 //  gLogVerbosity.compiler = true;  // TODO: don't check this in!
 //  gLogVerbosity.verifier = true;  // TODO: don't check this in!
@@ -721,28 +706,22 @@
     } else if (StartsWith(option, "-Xprofile-backoff:")) {
       parsed->profile_backoff_coefficient_ = ParseDoubleOrDie(
           option, ':', 1.0, 10.0, ignore_unrecognized, parsed->profile_backoff_coefficient_);
-    } else if (option == "-compiler-filter:interpret-only") {
-      parsed->compiler_filter_ = kInterpretOnly;
-    } else if (option == "-compiler-filter:space") {
-      parsed->compiler_filter_ = kSpace;
-    } else if (option == "-compiler-filter:balanced") {
-      parsed->compiler_filter_ = kBalanced;
-    } else if (option == "-compiler-filter:speed") {
-      parsed->compiler_filter_ = kSpeed;
-    } else if (option == "-compiler-filter:everything") {
-      parsed->compiler_filter_ = kEverything;
-    } else if (option == "-sea_ir") {
-      parsed->sea_ir_mode_ = true;
-    } else if (StartsWith(option, "-huge-method-max:")) {
-      parsed->huge_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-large-method-max:")) {
-      parsed->large_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-small-method-max:")) {
-      parsed->small_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-tiny-method-max:")) {
-      parsed->tiny_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-num-dex-methods-max:")) {
-      parsed->num_dex_methods_threshold_ = ParseIntegerOrDie(option, ':');
+    } else if (option == "-Xcompiler-option") {
+      i++;
+      if (i == options.size()) {
+        // TODO: usage
+        LOG(FATAL) << "Missing required compiler option for " << option;
+        return NULL;
+      }
+      parsed->compiler_options_.push_back(options[i].first);
+    } else if (option == "-Ximage-compiler-option") {
+      i++;
+      if (i == options.size()) {
+        // TODO: usage
+        LOG(FATAL) << "Missing required compiler option for " << option;
+        return NULL;
+      }
+      parsed->image_compiler_options_.push_back(options[i].first);
     } else {
       if (!ignore_unrecognized) {
         // TODO: print usage via vfprintf
@@ -988,14 +967,6 @@
   is_zygote_ = options->is_zygote_;
   is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
 
-  compiler_filter_ = options->compiler_filter_;
-  huge_method_threshold_ = options->huge_method_threshold_;
-  large_method_threshold_ = options->large_method_threshold_;
-  small_method_threshold_ = options->small_method_threshold_;
-  tiny_method_threshold_ = options->tiny_method_threshold_;
-  num_dex_methods_threshold_ = options->num_dex_methods_threshold_;
-
-  sea_ir_mode_ = options->sea_ir_mode_;
   vfprintf_ = options->hook_vfprintf_;
   exit_ = options->hook_exit_;
   abort_ = options->hook_abort_;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 223b8d5..8924921 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -72,26 +72,6 @@
  public:
   typedef std::vector<std::pair<std::string, const void*> > Options;
 
-  enum CompilerFilter {
-    kInterpretOnly,       // Compile nothing.
-    kSpace,               // Maximize space savings.
-    kBalanced,            // Try to get the best performance return on compilation investment.
-    kSpeed,               // Maximize runtime performance.
-    kEverything           // Force compilation (Note: excludes compilaton of class initializers).
-  };
-
-  // Guide heuristics to determine whether to compile method if profile data not available.
-#if ART_SMALL_MODE
-  static const CompilerFilter kDefaultCompilerFilter = kInterpretOnly;
-#else
-  static const CompilerFilter kDefaultCompilerFilter = kSpeed;
-#endif
-  static const size_t kDefaultHugeMethodThreshold = 10000;
-  static const size_t kDefaultLargeMethodThreshold = 600;
-  static const size_t kDefaultSmallMethodThreshold = 60;
-  static const size_t kDefaultTinyMethodThreshold = 20;
-  static const size_t kDefaultNumDexMethodsThreshold = 900;
-
   class ParsedOptions {
    public:
     // returns null if problem parsing and ignore_unrecognized is false
@@ -140,13 +120,8 @@
     void (*hook_exit_)(jint status);
     void (*hook_abort_)();
     std::vector<std::string> properties_;
-    CompilerFilter compiler_filter_;
-    size_t huge_method_threshold_;
-    size_t large_method_threshold_;
-    size_t small_method_threshold_;
-    size_t tiny_method_threshold_;
-    size_t num_dex_methods_threshold_;
-    bool sea_ir_mode_;
+    std::vector<std::string> compiler_options_;
+    std::vector<std::string> image_compiler_options_;
     bool profile_;
     std::string profile_output_filename_;
     int profile_period_s_;
@@ -178,42 +153,12 @@
     return is_explicit_gc_disabled_;
   }
 
-#ifdef ART_SEA_IR_MODE
-  bool IsSeaIRMode() const {
-    return sea_ir_mode_;
-  }
-#endif
-
-  void SetSeaIRMode(bool sea_ir_mode) {
-    sea_ir_mode_ = sea_ir_mode;
+  const std::vector<std::string>& GetCompilerOptions() const {
+    return compiler_options_;
   }
 
-  CompilerFilter GetCompilerFilter() const {
-    return compiler_filter_;
-  }
-
-  void SetCompilerFilter(CompilerFilter compiler_filter) {
-    compiler_filter_ = compiler_filter;
-  }
-
-  size_t GetHugeMethodThreshold() const {
-    return huge_method_threshold_;
-  }
-
-  size_t GetLargeMethodThreshold() const {
-    return large_method_threshold_;
-  }
-
-  size_t GetSmallMethodThreshold() const {
-    return small_method_threshold_;
-  }
-
-  size_t GetTinyMethodThreshold() const {
-    return tiny_method_threshold_;
-  }
-
-  size_t GetNumDexMethodsThreshold() const {
-      return num_dex_methods_threshold_;
+  const std::vector<std::string>& GetImageCompilerOptions() const {
+    return image_compiler_options_;
   }
 
   const std::string& GetHostPrefix() const {
@@ -525,14 +470,8 @@
   bool is_concurrent_gc_enabled_;
   bool is_explicit_gc_disabled_;
 
-  CompilerFilter compiler_filter_;
-  size_t huge_method_threshold_;
-  size_t large_method_threshold_;
-  size_t small_method_threshold_;
-  size_t tiny_method_threshold_;
-  size_t num_dex_methods_threshold_;
-
-  bool sea_ir_mode_;
+  std::vector<std::string> compiler_options_;
+  std::vector<std::string> image_compiler_options_;
 
   // The host prefix is used during cross compilation. It is removed
   // from the start of host paths such as:
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 2f959db..f0f5ed2 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -122,6 +122,7 @@
       : ScopedThreadStateChange(ThreadForEnv(env), kRunnable),
         env_(down_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
     self_->VerifyStack();
+    Locks::mutator_lock_->AssertSharedHeld(self_);
   }
 
   explicit ScopedObjectAccessUnchecked(Thread* self)
@@ -130,6 +131,7 @@
         env_(down_cast<JNIEnvExt*>(self->GetJniEnv())),
         vm_(env_ != NULL ? env_->vm : NULL) {
     self_->VerifyStack();
+    Locks::mutator_lock_->AssertSharedHeld(self_);
   }
 
   // Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
@@ -139,6 +141,7 @@
 
   // Here purely to force inlining.
   ~ScopedObjectAccessUnchecked() ALWAYS_INLINE {
+    Locks::mutator_lock_->AssertSharedHeld(self_);
   }
 
   JNIEnvExt* Env() const {
@@ -250,14 +253,12 @@
       LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
       SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
       : ScopedObjectAccessUnchecked(env) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
   }
 
   explicit ScopedObjectAccess(Thread* self)
       LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
       SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
       : ScopedObjectAccessUnchecked(self) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
   }
 
   ~ScopedObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 8949a5b..8b93b91 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1529,7 +1529,7 @@
   }
   SirtRef<mirror::ClassLoader> class_loader(this, cl);
   SirtRef<mirror::Class>
-      exception_class(this, runtime->GetClassLinker()->FindClass(exception_class_descriptor,
+      exception_class(this, runtime->GetClassLinker()->FindClass(this, exception_class_descriptor,
                                                                  class_loader));
   if (UNLIKELY(exception_class.get() == nullptr)) {
     CHECK(IsExceptionPending());
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 74e6f1c..d311945 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -269,6 +269,7 @@
 
 void ThreadList::SuspendAll() {
   Thread* self = Thread::Current();
+  DCHECK(self != nullptr);
 
   VLOG(threads) << *self << " SuspendAll starting...";
 
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index dcfa24b..9dc7b44 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -29,7 +29,7 @@
 TEST_F(TransactionTest, Object_class) {
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindSystemClass("Ljava/lang/Object;"));
+                                    class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
 
   Transaction transaction;
@@ -47,7 +47,8 @@
 TEST_F(TransactionTest, Object_monitor) {
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindSystemClass("Ljava/lang/Object;"));
+                                    class_linker_->FindSystemClass(soa.Self(),
+                                                                   "Ljava/lang/Object;"));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
   ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -74,7 +75,8 @@
 TEST_F(TransactionTest, Array_length) {
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindSystemClass("[Ljava/lang/Object;"));
+                                    class_linker_->FindSystemClass(soa.Self(),
+                                                                   "[Ljava/lang/Object;"));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
 
   constexpr int32_t kArraySize = 2;
@@ -101,7 +103,8 @@
   ASSERT_TRUE(class_loader.get() != nullptr);
 
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindClass("LStaticFieldsTest;", class_loader));
+                                    class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;",
+                                                             class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->EnsureInitialized(sirt_klass, true, true);
   ASSERT_TRUE(sirt_klass->IsInitialized());
@@ -147,13 +150,16 @@
   ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
   ASSERT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast<double>(0.0));
 
-  mirror::ArtField* objectField = sirt_klass->FindDeclaredStaticField("objectField", "Ljava/lang/Object;");
+  mirror::ArtField* objectField = sirt_klass->FindDeclaredStaticField("objectField",
+                                                                      "Ljava/lang/Object;");
   ASSERT_TRUE(objectField != nullptr);
   ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot);
   ASSERT_EQ(objectField->GetObject(sirt_klass.get()), nullptr);
 
   // Create a java.lang.Object instance to set objectField.
-  SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  SirtRef<mirror::Class> object_klass(soa.Self(),
+                                      class_linker_->FindSystemClass(soa.Self(),
+                                                                     "Ljava/lang/Object;"));
   ASSERT_TRUE(object_klass.get() != nullptr);
   SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
   ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -193,7 +199,8 @@
   ASSERT_TRUE(class_loader.get() != nullptr);
 
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindClass("LInstanceFieldsTest;", class_loader));
+                                    class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;",
+                                                             class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->EnsureInitialized(sirt_klass, true, true);
   ASSERT_TRUE(sirt_klass->IsInitialized());
@@ -243,13 +250,16 @@
   ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
   ASSERT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast<double>(0.0));
 
-  mirror::ArtField* objectField = sirt_klass->FindDeclaredInstanceField("objectField", "Ljava/lang/Object;");
+  mirror::ArtField* objectField = sirt_klass->FindDeclaredInstanceField("objectField",
+                                                                        "Ljava/lang/Object;");
   ASSERT_TRUE(objectField != nullptr);
   ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot);
   ASSERT_EQ(objectField->GetObject(sirt_instance.get()), nullptr);
 
   // Create a java.lang.Object instance to set objectField.
-  SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  SirtRef<mirror::Class> object_klass(soa.Self(),
+                                      class_linker_->FindSystemClass(soa.Self(),
+                                                                     "Ljava/lang/Object;"));
   ASSERT_TRUE(object_klass.get() != nullptr);
   SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
   ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -290,7 +300,8 @@
   ASSERT_TRUE(class_loader.get() != nullptr);
 
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindClass("LStaticArrayFieldsTest;", class_loader));
+                                    class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;",
+                                                             class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->EnsureInitialized(sirt_klass, true, true);
   ASSERT_TRUE(sirt_klass->IsInitialized());
@@ -352,15 +363,19 @@
   ASSERT_EQ(doubleArray->GetLength(), 1);
   ASSERT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
 
-  mirror::ArtField* objectArrayField = sirt_klass->FindDeclaredStaticField("objectArrayField", "[Ljava/lang/Object;");
+  mirror::ArtField* objectArrayField = sirt_klass->FindDeclaredStaticField("objectArrayField",
+                                                                           "[Ljava/lang/Object;");
   ASSERT_TRUE(objectArrayField != nullptr);
-  mirror::ObjectArray<mirror::Object>* objectArray = objectArrayField->GetObject(sirt_klass.get())->AsObjectArray<mirror::Object>();
+  mirror::ObjectArray<mirror::Object>* objectArray =
+      objectArrayField->GetObject(sirt_klass.get())->AsObjectArray<mirror::Object>();
   ASSERT_TRUE(objectArray != nullptr);
   ASSERT_EQ(objectArray->GetLength(), 1);
   ASSERT_EQ(objectArray->GetWithoutChecks(0), nullptr);
 
   // Create a java.lang.Object instance to set objectField.
-  SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  SirtRef<mirror::Class> object_klass(soa.Self(),
+                                      class_linker_->FindSystemClass(soa.Self(),
+                                                                     "Ljava/lang/Object;"));
   ASSERT_TRUE(object_klass.get() != nullptr);
   SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
   ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -400,7 +415,9 @@
   ASSERT_TRUE(class_loader.get() != nullptr);
 
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindClass("LTransaction$EmptyStatic;", class_loader));
+                                    class_linker_->FindClass(soa.Self(),
+                                                             "LTransaction$EmptyStatic;",
+                                                             class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
@@ -419,7 +436,9 @@
   ASSERT_TRUE(class_loader.get() != nullptr);
 
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindClass("LTransaction$StaticFieldClass;", class_loader));
+                                    class_linker_->FindClass(soa.Self(),
+                                                             "LTransaction$StaticFieldClass;",
+                                                             class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
@@ -441,22 +460,25 @@
   // Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will
   // be thrown by class initialization due to native call.
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindSystemClass("Ljava/lang/ExceptionInInitializerError;"));
+                                    class_linker_->FindSystemClass(soa.Self(),
+                                                                   "Ljava/lang/ExceptionInInitializerError;"));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
-  sirt_klass.reset(class_linker_->FindSystemClass("Ljava/lang/InternalError;"));
+  sirt_klass.reset(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;"));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
 
   // Load and verify Transaction$NativeSupport used in class initialization.
-  sirt_klass.reset(class_linker_->FindClass("LTransaction$NativeSupport;", class_loader));
+  sirt_klass.reset(class_linker_->FindClass(soa.Self(), "LTransaction$NativeSupport;",
+                                            class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
 
-  sirt_klass.reset(class_linker_->FindClass("LTransaction$BlacklistedClass;", class_loader));
+  sirt_klass.reset(class_linker_->FindClass(soa.Self(), "LTransaction$BlacklistedClass;",
+                                            class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
diff --git a/runtime/utils.cc b/runtime/utils.cc
index aad21bc..8e6ddaf 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -24,6 +24,7 @@
 #include <unistd.h>
 
 #include "UniquePtr.h"
+#include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "dex_file-inl.h"
 #include "mirror/art_field-inl.h"
@@ -1203,4 +1204,56 @@
                  sizeof(OatHeader::kOatMagic)) == 0);
 }
 
+bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) {
+  const std::string command_line(Join(arg_vector, ' '));
+
+  CHECK_GE(arg_vector.size(), 1U) << command_line;
+
+  // Convert the args to char pointers.
+  const char* program = arg_vector[0].c_str();
+  std::vector<char*> args;
+  for (std::vector<std::string>::const_iterator it = arg_vector.begin(); it != arg_vector.end();
+      ++it) {
+    CHECK(*it != nullptr);
+    args.push_back(const_cast<char*>(it->c_str()));
+  }
+  args.push_back(NULL);
+
+  // fork and exec
+  pid_t pid = fork();
+  if (pid == 0) {
+    // no allocation allowed between fork and exec
+
+    // change process groups, so we don't get reaped by ProcessManager
+    setpgid(0, 0);
+
+    execv(program, &args[0]);
+
+    *error_msg = StringPrintf("Failed to execv(%s): %s", command_line.c_str(), strerror(errno));
+    return false;
+  } else {
+    if (pid == -1) {
+      *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
+                                command_line.c_str(), strerror(errno));
+      return false;
+    }
+
+    // wait for subprocess to finish
+    int status;
+    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+    if (got_pid != pid) {
+      *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
+                                "wanted %d, got %d: %s",
+                                command_line.c_str(), pid, got_pid, strerror(errno));
+      return false;
+    }
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+      *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+                                command_line.c_str());
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index e2d8966..0bb06de 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -396,6 +396,9 @@
 bool IsDexMagic(uint32_t magic);
 bool IsOatMagic(uint32_t magic);
 
+// Wrapper on fork/execv to run a command in a subprocess.
+bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg);
+
 class VoidFunctor {
  public:
   template <typename A>
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index b43177b..0d237e2 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -99,7 +99,7 @@
   SirtRef<mirror::ShortArray> a(soa.Self(), mirror::ShortArray::Alloc(soa.Self(), 2));
   EXPECT_EQ("short[]", PrettyTypeOf(a.get()));
 
-  mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/String;");
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
   ASSERT_TRUE(c != NULL);
   mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
   EXPECT_EQ("java.lang.String[]", PrettyTypeOf(o));
@@ -109,7 +109,7 @@
 TEST_F(UtilsTest, PrettyClass) {
   ScopedObjectAccess soa(Thread::Current());
   EXPECT_EQ("null", PrettyClass(NULL));
-  mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/String;");
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
   ASSERT_TRUE(c != NULL);
   mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
   EXPECT_EQ("java.lang.Class<java.lang.String[]>", PrettyClass(o->GetClass()));
@@ -118,7 +118,7 @@
 TEST_F(UtilsTest, PrettyClassAndClassLoader) {
   ScopedObjectAccess soa(Thread::Current());
   EXPECT_EQ("null", PrettyClassAndClassLoader(NULL));
-  mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/String;");
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
   ASSERT_TRUE(c != NULL);
   mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
   EXPECT_EQ("java.lang.Class<java.lang.String[],null>", PrettyClassAndClassLoader(o->GetClass()));
@@ -128,7 +128,8 @@
   ScopedObjectAccess soa(Thread::Current());
   EXPECT_EQ("null", PrettyField(NULL));
 
-  mirror::Class* java_lang_String = class_linker_->FindSystemClass("Ljava/lang/String;");
+  mirror::Class* java_lang_String = class_linker_->FindSystemClass(soa.Self(),
+                                                                   "Ljava/lang/String;");
 
   mirror::ArtField* f;
   f = java_lang_String->FindDeclaredInstanceField("count", "I");
@@ -197,7 +198,7 @@
 
 TEST_F(UtilsTest, JniShortName_JniLongName) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* c = class_linker_->FindSystemClass("Ljava/lang/String;");
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;");
   ASSERT_TRUE(c != NULL);
   mirror::ArtMethod* m;
 
@@ -349,4 +350,26 @@
   CheckGetDalvikCacheFilenameOrDie("/system/framework/boot.art", "system@framework@boot.art");
 }
 
+TEST_F(UtilsTest, ExecSuccess) {
+  std::vector<std::string> command;
+  if (kIsTargetBuild) {
+    command.push_back("/system/bin/id");
+  } else {
+    command.push_back("/usr/bin/id");
+  }
+  std::string error_msg;
+  EXPECT_TRUE(Exec(command, &error_msg));
+  EXPECT_EQ(0U, error_msg.size()) << error_msg;
+}
+
+// TODO: Disabled due to hang tearing down CommonTest.
+// Renable after splitting into RuntimeTest and CompilerTest.
+TEST_F(UtilsTest, DISABLED_ExecError) {
+  std::vector<std::string> command;
+  command.push_back("bogus");
+  std::string error_msg;
+  EXPECT_FALSE(Exec(command, &error_msg));
+  EXPECT_NE(0U, error_msg.size());
+}
+
 }  // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index ab943a6..ffa8b9e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3129,14 +3129,15 @@
     this_class = actual_arg_type.GetClass();
   } else {
     const std::string& descriptor(actual_arg_type.GetDescriptor());
+    Thread* self = Thread::Current();
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    this_class = class_linker->FindClass(descriptor.c_str(), *class_loader_);
+    this_class = class_linker->FindClass(self, descriptor.c_str(), *class_loader_);
     if (this_class == NULL) {
       Thread* self = Thread::Current();
       self->ClearException();
       // Look for a system class
       SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr);
-      this_class = class_linker->FindClass(descriptor.c_str(), null_class_loader);
+      this_class = class_linker->FindClass(self, descriptor.c_str(), null_class_loader);
     }
   }
   if (this_class == NULL) {
@@ -3638,7 +3639,7 @@
 // Returns the access field of a quick field access (iget/iput-quick) or NULL
 // if it cannot be found.
 mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst,
-                                                   RegisterLine* reg_line) {
+                                                      RegisterLine* reg_line) {
   DCHECK(inst->Opcode() == Instruction::IGET_QUICK ||
          inst->Opcode() == Instruction::IGET_WIDE_QUICK ||
          inst->Opcode() == Instruction::IGET_OBJECT_QUICK ||
@@ -3654,12 +3655,12 @@
     const std::string& descriptor(object_type.GetDescriptor());
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     Thread* self = Thread::Current();
-    object_class = class_linker->FindClass(descriptor.c_str(), *class_loader_);
+    object_class = class_linker->FindClass(self, descriptor.c_str(), *class_loader_);
     if (object_class == NULL) {
       self->ClearException();
       // Look for a system class
       SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr);
-      object_class = class_linker->FindClass(descriptor.c_str(), null_class_loader);
+      object_class = class_linker->FindClass(self, descriptor.c_str(), null_class_loader);
     }
   }
   if (object_class == NULL) {
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index a56abba..ffa2455 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -30,7 +30,7 @@
   void VerifyClass(const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     ASSERT_TRUE(descriptor != NULL);
-    mirror::Class* klass = class_linker_->FindSystemClass(descriptor.c_str());
+    mirror::Class* klass = class_linker_->FindSystemClass(Thread::Current(), descriptor.c_str());
 
     // Verify the class
     std::string error_msg;
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 630ef8a..63f0ff4 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -18,6 +18,7 @@
 
 
 #include "base/casts.h"
+#include "class_linker-inl.h"
 #include "dex_file-inl.h"
 #include "mirror/class.h"
 #include "mirror/class-inl.h"
@@ -928,11 +929,7 @@
     }
     mirror::Class* common_elem = ClassJoin(s_ct, t_ct);
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Thread* self = Thread::Current();
-    SirtRef<mirror::ClassLoader> class_loader(self, s->GetClassLoader());
-    std::string descriptor("[");
-    descriptor += ClassHelper(common_elem).GetDescriptor();
-    mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+    mirror::Class* array_class = class_linker->FindArrayClass(Thread::Current(), common_elem);
     DCHECK(array_class != NULL);
     return array_class;
   } else {
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 5e894ed..9dd57b8 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -17,6 +17,7 @@
 #include "reg_type_cache-inl.h"
 
 #include "base/casts.h"
+#include "class_linker-inl.h"
 #include "dex_file-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
@@ -140,10 +141,11 @@
   // Class was not found, must create new type.
   // Try resolving class
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  SirtRef<mirror::ClassLoader> class_loader(Thread::Current(), loader);
+  Thread* self = Thread::Current();
+  SirtRef<mirror::ClassLoader> class_loader(self, loader);
   mirror::Class* klass = NULL;
   if (can_load_classes_) {
-    klass = class_linker->FindClass(descriptor, class_loader);
+    klass = class_linker->FindClass(self, descriptor, class_loader);
   } else {
     klass = class_linker->LookupClass(descriptor, loader);
     if (klass != NULL && !klass->IsLoaded()) {
@@ -277,7 +279,8 @@
   mirror::Class* klass = NULL;
   // Try loading the class from linker.
   if (!descriptor.empty()) {
-    klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(descriptor.c_str());
+    klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(),
+                                                                       descriptor.c_str());
   }
   Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
   RegTypeCache::primitive_count_++;