Merge "Add even more logging failed to copy GC error"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c27f8db..ed34a8d 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -25,6 +25,7 @@
 GTEST_DEX_DIRECTORIES := \
   AbstractMethod \
   AllFields \
+  DefaultMethods \
   DexToDexDecompiler \
   ErroneousA \
   ErroneousB \
@@ -104,7 +105,7 @@
 ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex
 ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
-ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB
+ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods
 ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB
 ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
 ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 89e8a67..7ee494a 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -76,7 +76,7 @@
   void Compile(ImageHeader::StorageMode storage_mode,
                CompilationHelper& out_helper,
                const std::string& extra_dex = "",
-               const std::string& image_class = "");
+               const std::initializer_list<std::string>& image_classes = {});
 
   void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
     CommonCompilerTest::SetUpRuntimeOptions(options);
@@ -90,6 +90,18 @@
     return new std::unordered_set<std::string>(image_classes_);
   }
 
+  ArtMethod* FindCopiedMethod(ArtMethod* origin, mirror::Class* klass)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    PointerSize pointer_size = class_linker_->GetImagePointerSize();
+    for (ArtMethod& m : klass->GetCopiedMethods(pointer_size)) {
+      if (strcmp(origin->GetName(), m.GetName()) == 0 &&
+          origin->GetSignature() == m.GetSignature()) {
+        return &m;
+      }
+    }
+    return nullptr;
+  }
+
  private:
   std::unordered_set<std::string> image_classes_;
 };
@@ -345,8 +357,8 @@
 void ImageTest::Compile(ImageHeader::StorageMode storage_mode,
                         CompilationHelper& helper,
                         const std::string& extra_dex,
-                        const std::string& image_class) {
-  if (!image_class.empty()) {
+                        const std::initializer_list<std::string>& image_classes) {
+  for (const std::string& image_class : image_classes) {
     image_classes_.insert(image_class);
   }
   CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U);
@@ -358,13 +370,15 @@
     helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str());
   }
   helper.Compile(compiler_driver_.get(), storage_mode);
-  if (!image_class.empty()) {
+  if (image_classes.begin() != image_classes.end()) {
     // Make sure the class got initialized.
     ScopedObjectAccess soa(Thread::Current());
     ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
-    mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str());
-    EXPECT_TRUE(klass != nullptr);
-    EXPECT_TRUE(klass->IsInitialized());
+    for (const std::string& image_class : image_classes) {
+      mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str());
+      EXPECT_TRUE(klass != nullptr);
+      EXPECT_TRUE(klass->IsInitialized());
+    }
   }
 }
 
@@ -492,7 +506,7 @@
   // Compile multi-image with ImageLayoutA being the last image.
   {
     CompilationHelper helper;
-    Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutA", "LMyClass;");
+    Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutA", {"LMyClass;"});
     image_sizes = helper.GetImageObjectSectionSizes();
   }
   TearDown();
@@ -501,7 +515,7 @@
   // Compile multi-image with ImageLayoutB being the last image.
   {
     CompilationHelper helper;
-    Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutB", "LMyClass;");
+    Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutB", {"LMyClass;"});
     image_sizes_extra = helper.GetImageObjectSectionSizes();
   }
   // Make sure that the new stuff in the clinit in ImageLayoutB is in the last image and not in the
@@ -553,4 +567,63 @@
     ASSERT_FALSE(image_header.IsValid());
 }
 
+// Test that pointer to quick code is the same in
+// a default method of an interface and in a copied method
+// of a class which implements the interface. This should be true
+// only if the copied method and the origin method are located in the
+// same oat file.
+TEST_F(ImageTest, TestDefaultMethods) {
+  CompilationHelper helper;
+  Compile(ImageHeader::kStorageModeUncompressed,
+      helper,
+      "DefaultMethods",
+      {"LIface;", "LImpl;", "LIterableBase;"});
+
+  PointerSize pointer_size = class_linker_->GetImagePointerSize();
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  // Test the pointer to quick code is the same in origin method
+  // and in the copied method form the same oat file.
+  mirror::Class* iface_klass = class_linker_->LookupClass(
+      self, "LIface;", ObjPtr<mirror::ClassLoader>());
+  ASSERT_NE(nullptr, iface_klass);
+  ArtMethod* origin = iface_klass->FindDeclaredVirtualMethod(
+      "defaultMethod", "()V", pointer_size);
+  ASSERT_NE(nullptr, origin);
+  const void* code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
+  // The origin method should have a pointer to quick code
+  ASSERT_NE(nullptr, code);
+  ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code));
+  mirror::Class* impl_klass = class_linker_->LookupClass(
+      self, "LImpl;", ObjPtr<mirror::ClassLoader>());
+  ASSERT_NE(nullptr, impl_klass);
+  ArtMethod* copied = FindCopiedMethod(origin, impl_klass);
+  ASSERT_NE(nullptr, copied);
+  // the copied method should have pointer to the same quick code as the origin method
+  ASSERT_EQ(code, copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size));
+
+  // Test the origin method has pointer to quick code
+  // but the copied method has pointer to interpreter
+  // because these methods are in different oat files.
+  mirror::Class* iterable_klass = class_linker_->LookupClass(
+      self, "Ljava/lang/Iterable;", ObjPtr<mirror::ClassLoader>());
+  ASSERT_NE(nullptr, iterable_klass);
+  origin = iterable_klass->FindDeclaredVirtualMethod(
+      "forEach", "(Ljava/util/function/Consumer;)V", pointer_size);
+  ASSERT_NE(nullptr, origin);
+  code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
+  // the origin method should have a pointer to quick code
+  ASSERT_NE(nullptr, code);
+  ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code));
+  mirror::Class* iterablebase_klass = class_linker_->LookupClass(
+      self, "LIterableBase;", ObjPtr<mirror::ClassLoader>());
+  ASSERT_NE(nullptr, iterablebase_klass);
+  copied = FindCopiedMethod(origin, iterablebase_klass);
+  ASSERT_NE(nullptr, copied);
+  code = copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
+  // the copied method should have a pointer to interpreter
+  ASSERT_TRUE(class_linker_->IsQuickToInterpreterBridge(code));
+}
+
 }  // namespace art
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 5406ae7..8e25aa3 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1034,18 +1034,63 @@
 
 class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
  public:
-  InitImageMethodVisitor(OatWriter* writer, size_t offset)
+  InitImageMethodVisitor(OatWriter* writer,
+                         size_t offset,
+                         const std::vector<const DexFile*>* dex_files)
     : OatDexMethodVisitor(writer, offset),
-      pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())) {
+      pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())),
+      dex_files_(dex_files),
+      class_linker_(Runtime::Current()->GetClassLinker()) {
+    }
+
+  // Handle copied methods here. Copy pointer to quick code from
+  // an origin method to a copied method only if they are
+  // in the same oat file. If the origin and the copied methods are
+  // in different oat files don't touch the copied method.
+  // References to other oat files are not supported yet.
+  bool StartClass(const DexFile* dex_file, size_t class_def_index)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    OatDexMethodVisitor::StartClass(dex_file, class_def_index);
+    // Skip classes that are not in the image.
+    if (!IsImageClass()) {
+      return true;
+    }
+    ScopedObjectAccessUnchecked soa(Thread::Current());
+    StackHandleScope<1> hs(soa.Self());
+    Handle<mirror::DexCache> dex_cache = hs.NewHandle(
+        class_linker_->FindDexCache(Thread::Current(), *dex_file));
+    const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+    mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_);
+    if (klass != nullptr) {
+      for (ArtMethod& method : klass->GetCopiedMethods(pointer_size_)) {
+        // Find origin method. Declaring class and dex_method_idx
+        // in the copied method should be the same as in the origin
+        // method.
+        mirror::Class* declaring_class = method.GetDeclaringClass();
+        ArtMethod* origin = declaring_class->FindDeclaredVirtualMethod(
+            declaring_class->GetDexCache(),
+            method.GetDexMethodIndex(),
+            pointer_size_);
+        CHECK(origin != nullptr);
+        if (IsInOatFile(&declaring_class->GetDexFile())) {
+          const void* code_ptr =
+              origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
+          if (code_ptr == nullptr) {
+            methods_to_process_.push_back(std::make_pair(&method, origin));
+          } else {
+            method.SetEntryPointFromQuickCompiledCodePtrSize(
+                code_ptr, pointer_size_);
+          }
+        }
+      }
+    }
+    return true;
   }
 
   bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    const DexFile::TypeId& type_id =
-        dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_);
-    const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id);
     // Skip methods that are not in the image.
-    if (!writer_->GetCompilerDriver()->IsImageClass(class_descriptor)) {
+    if (!IsImageClass()) {
       return true;
     }
 
@@ -1059,17 +1104,16 @@
       ++method_offsets_index_;
     }
 
-    ClassLinker* linker = Runtime::Current()->GetClassLinker();
     // Unchecked as we hold mutator_lock_ on entry.
     ScopedObjectAccessUnchecked soa(Thread::Current());
     StackHandleScope<1> hs(soa.Self());
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache(
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker_->FindDexCache(
         Thread::Current(), *dex_file_)));
     ArtMethod* method;
     if (writer_->HasBootImage()) {
       const InvokeType invoke_type = it.GetMethodInvokeType(
           dex_file_->GetClassDef(class_def_index_));
-      method = linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
+      method = class_linker_->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
           *dex_file_,
           it.GetMemberIndex(),
           dex_cache,
@@ -1089,7 +1133,8 @@
       // Should already have been resolved by the compiler, just peek into the dex cache.
       // It may not be resolved if the class failed to verify, in this case, don't set the
       // entrypoint. This is not fatal since the dex cache will contain a resolution method.
-      method = dex_cache->GetResolvedMethod(it.GetMemberIndex(), linker->GetImagePointerSize());
+      method = dex_cache->GetResolvedMethod(it.GetMemberIndex(),
+          class_linker_->GetImagePointerSize());
     }
     if (method != nullptr &&
         compiled_method != nullptr &&
@@ -1101,8 +1146,38 @@
     return true;
   }
 
+  // Check whether current class is image class
+  bool IsImageClass() {
+    const DexFile::TypeId& type_id =
+        dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_);
+    const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id);
+    return writer_->GetCompilerDriver()->IsImageClass(class_descriptor);
+  }
+
+  // Check whether specified dex file is in the compiled oat file.
+  bool IsInOatFile(const DexFile* dex_file) {
+    return ContainsElement(*dex_files_, dex_file);
+  }
+
+  // Assign a pointer to quick code for copied methods
+  // not handled in the method StartClass
+  void Postprocess() {
+    for (std::pair<ArtMethod*, ArtMethod*>& p : methods_to_process_) {
+      ArtMethod* method = p.first;
+      ArtMethod* origin = p.second;
+      const void* code_ptr =
+          origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
+      if (code_ptr != nullptr) {
+        method->SetEntryPointFromQuickCompiledCodePtrSize(code_ptr, pointer_size_);
+      }
+    }
+  }
+
  protected:
   const PointerSize pointer_size_;
+  const std::vector<const DexFile*>* dex_files_;
+  ClassLinker* const class_linker_;
+  std::vector<std::pair<ArtMethod*, ArtMethod*>> methods_to_process_;
 };
 
 class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
@@ -1744,8 +1819,9 @@
   offset = code_visitor.GetOffset();
 
   if (HasImage()) {
-    InitImageMethodVisitor image_visitor(this, offset);
+    InitImageMethodVisitor image_visitor(this, offset, dex_files_);
     success = VisitDexMethods(&image_visitor);
+    image_visitor.Postprocess();
     DCHECK(success);
     offset = image_visitor.GetOffset();
   }
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index e34f116..caea250 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1875,6 +1875,7 @@
 
 Label* CodeGeneratorARM::GetFinalLabel(HInstruction* instruction, Label* final_label) {
   DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
+  DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall());
 
   const HBasicBlock* const block = instruction->GetBlock();
   const HLoopInformation* const info = block->GetLoopInformation();
@@ -2901,16 +2902,20 @@
 
   // Convert the jumps into the result.
   Label done_label;
+  Label* final_label = codegen_->GetFinalLabel(cond, &done_label);
 
   // False case: result = 0.
   __ Bind(&false_label);
   __ LoadImmediate(out, 0);
-  __ b(&done_label);
+  __ b(final_label);
 
   // True case: result = 1.
   __ Bind(&true_label);
   __ LoadImmediate(out, 1);
-  __ Bind(&done_label);
+
+  if (done_label.IsLinked()) {
+    __ Bind(&done_label);
+  }
 }
 
 void LocationsBuilderARM::VisitEqual(HEqual* comp) {
@@ -4441,7 +4446,8 @@
 // rotates by swapping input regs (effectively rotating by the first 32-bits of
 // a larger rotation) or flipping direction (thus treating larger right/left
 // rotations as sub-word sized rotations in the other direction) as appropriate.
-void InstructionCodeGeneratorARM::HandleLongRotate(LocationSummary* locations) {
+void InstructionCodeGeneratorARM::HandleLongRotate(HRor* ror) {
+  LocationSummary* locations = ror->GetLocations();
   Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
   Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
   Location rhs = locations->InAt(1);
@@ -4474,6 +4480,7 @@
     Register shift_left = locations->GetTemp(1).AsRegister<Register>();
     Label end;
     Label shift_by_32_plus_shift_right;
+    Label* final_label = codegen_->GetFinalLabel(ror, &end);
 
     __ and_(shift_right, rhs.AsRegister<Register>(), ShifterOperand(0x1F));
     __ Lsrs(shift_left, rhs.AsRegister<Register>(), 6);
@@ -4488,7 +4495,7 @@
     __ Lsl(out_reg_lo, in_reg_lo, shift_left);
     __ Lsr(shift_left, in_reg_hi, shift_right);
     __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_left));
-    __ b(&end);
+    __ b(final_label);
 
     __ Bind(&shift_by_32_plus_shift_right);  // Shift by 32+shift_right.
     // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
@@ -4500,7 +4507,9 @@
     __ Lsl(shift_right, in_reg_hi, shift_left);
     __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_right));
 
-    __ Bind(&end);
+    if (end.IsLinked()) {
+      __ Bind(&end);
+    }
   }
 }
 
@@ -4540,7 +4549,7 @@
       break;
     }
     case Primitive::kPrimLong: {
-      HandleLongRotate(locations);
+      HandleLongRotate(ror);
       break;
     }
     default:
@@ -4919,6 +4928,7 @@
   Location right = locations->InAt(1);
 
   Label less, greater, done;
+  Label* final_label = codegen_->GetFinalLabel(compare, &done);
   Primitive::Type type = compare->InputAt(0)->GetType();
   Condition less_cond;
   switch (type) {
@@ -4958,17 +4968,19 @@
       UNREACHABLE();
   }
 
-  __ b(&done, EQ);
+  __ b(final_label, EQ);
   __ b(&less, less_cond);
 
   __ Bind(&greater);
   __ LoadImmediate(out, 1);
-  __ b(&done);
+  __ b(final_label);
 
   __ Bind(&less);
   __ LoadImmediate(out, -1);
 
-  __ Bind(&done);
+  if (done.IsLinked()) {
+    __ Bind(&done);
+  }
 }
 
 void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
@@ -5746,6 +5758,7 @@
         int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
         if (maybe_compressed_char_at) {
           Label uncompressed_load, done;
+          Label* final_label = codegen_->GetFinalLabel(instruction, &done);
           __ Lsrs(length, length, 1u);  // LSRS has a 16-bit encoding, TST (immediate) does not.
           static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
                         "Expecting 0=compressed, 1=uncompressed");
@@ -5754,13 +5767,15 @@
                             out_loc.AsRegister<Register>(),
                             obj,
                             data_offset + const_index);
-          __ b(&done);
+          __ b(final_label);
           __ Bind(&uncompressed_load);
           __ LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar),
                             out_loc.AsRegister<Register>(),
                             obj,
                             data_offset + (const_index << 1));
-          __ Bind(&done);
+          if (done.IsLinked()) {
+            __ Bind(&done);
+          }
         } else {
           uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
 
@@ -5784,17 +5799,20 @@
         }
         if (maybe_compressed_char_at) {
           Label uncompressed_load, done;
+          Label* final_label = codegen_->GetFinalLabel(instruction, &done);
           __ Lsrs(length, length, 1u);  // LSRS has a 16-bit encoding, TST (immediate) does not.
           static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
                         "Expecting 0=compressed, 1=uncompressed");
           __ b(&uncompressed_load, CS);
           __ ldrb(out_loc.AsRegister<Register>(),
                   Address(temp, index.AsRegister<Register>(), Shift::LSL, 0));
-          __ b(&done);
+          __ b(final_label);
           __ Bind(&uncompressed_load);
           __ ldrh(out_loc.AsRegister<Register>(),
                   Address(temp, index.AsRegister<Register>(), Shift::LSL, 1));
-          __ Bind(&done);
+          if (done.IsLinked()) {
+            __ Bind(&done);
+          }
         } else {
           codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, index.AsRegister<Register>());
         }
@@ -6019,6 +6037,7 @@
       uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
       uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
       Label done;
+      Label* final_label = codegen_->GetFinalLabel(instruction, &done);
       SlowPathCodeARM* slow_path = nullptr;
 
       if (may_need_runtime_call_for_type_check) {
@@ -6040,7 +6059,7 @@
                                               index.AsRegister<Register>());
           }
           codegen_->MaybeRecordImplicitNullCheck(instruction);
-          __ b(&done);
+          __ b(final_label);
           __ Bind(&non_zero);
         }
 
@@ -7021,6 +7040,7 @@
   uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
   uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
   Label done, zero;
+  Label* final_label = codegen_->GetFinalLabel(instruction, &done);
   SlowPathCodeARM* slow_path = nullptr;
 
   // Return 0 if `obj` is null.
@@ -7042,7 +7062,7 @@
       // Classes must be equal for the instanceof to succeed.
       __ b(&zero, NE);
       __ LoadImmediate(out, 1);
-      __ b(&done);
+      __ b(final_label);
       break;
     }
 
@@ -7065,12 +7085,12 @@
                                        maybe_temp_loc,
                                        kCompilerReadBarrierOption);
       // If `out` is null, we use it for the result, and jump to `done`.
-      __ CompareAndBranchIfZero(out, &done);
+      __ CompareAndBranchIfZero(out, final_label);
       __ cmp(out, ShifterOperand(cls));
       __ b(&loop, NE);
       __ LoadImmediate(out, 1);
       if (zero.IsLinked()) {
-        __ b(&done);
+        __ b(final_label);
       }
       break;
     }
@@ -7096,11 +7116,11 @@
                                        kCompilerReadBarrierOption);
       __ CompareAndBranchIfNonZero(out, &loop);
       // If `out` is null, we use it for the result, and jump to `done`.
-      __ b(&done);
+      __ b(final_label);
       __ Bind(&success);
       __ LoadImmediate(out, 1);
       if (zero.IsLinked()) {
-        __ b(&done);
+        __ b(final_label);
       }
       break;
     }
@@ -7125,13 +7145,13 @@
                                        maybe_temp_loc,
                                        kCompilerReadBarrierOption);
       // If `out` is null, we use it for the result, and jump to `done`.
-      __ CompareAndBranchIfZero(out, &done);
+      __ CompareAndBranchIfZero(out, final_label);
       __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
       __ CompareAndBranchIfNonZero(out, &zero);
       __ Bind(&exact_check);
       __ LoadImmediate(out, 1);
-      __ b(&done);
+      __ b(final_label);
       break;
     }
 
@@ -7152,7 +7172,7 @@
       __ b(slow_path->GetEntryLabel(), NE);
       __ LoadImmediate(out, 1);
       if (zero.IsLinked()) {
-        __ b(&done);
+        __ b(final_label);
       }
       break;
     }
@@ -7183,7 +7203,7 @@
       codegen_->AddSlowPath(slow_path);
       __ b(slow_path->GetEntryLabel());
       if (zero.IsLinked()) {
-        __ b(&done);
+        __ b(final_label);
       }
       break;
     }
@@ -7269,9 +7289,10 @@
   codegen_->AddSlowPath(type_check_slow_path);
 
   Label done;
+  Label* final_label = codegen_->GetFinalLabel(instruction, &done);
   // Avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
-    __ CompareAndBranchIfZero(obj, &done);
+    __ CompareAndBranchIfZero(obj, final_label);
   }
 
   switch (type_check_kind) {
@@ -7335,7 +7356,7 @@
       Label loop;
       __ Bind(&loop);
       __ cmp(temp, ShifterOperand(cls));
-      __ b(&done, EQ);
+      __ b(final_label, EQ);
 
       // /* HeapReference<Class> */ temp = temp->super_class_
       GenerateReferenceLoadOneRegister(instruction,
@@ -7363,7 +7384,7 @@
 
       // Do an exact check.
       __ cmp(temp, ShifterOperand(cls));
-      __ b(&done, EQ);
+      __ b(final_label, EQ);
 
       // Otherwise, we need to check that the object's class is a non-primitive array.
       // /* HeapReference<Class> */ temp = temp->component_type_
@@ -7433,7 +7454,10 @@
       break;
     }
   }
-  __ Bind(&done);
+
+  if (done.IsLinked()) {
+    __ Bind(&done);
+  }
 
   __ Bind(type_check_slow_path->GetExitLabel());
 }
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 5b15902..59a7f7c 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -237,7 +237,7 @@
   void HandleBitwiseOperation(HBinaryOperation* operation);
   void HandleCondition(HCondition* condition);
   void HandleIntegerRotate(LocationSummary* locations);
-  void HandleLongRotate(LocationSummary* locations);
+  void HandleLongRotate(HRor* ror);
   void HandleShift(HBinaryOperation* operation);
 
   void GenerateWideAtomicStore(Register addr, uint32_t offset,
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index d75779c..2d2d810 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -1950,6 +1950,7 @@
 vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction,
                                                    vixl32::Label* final_label) {
   DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
+  DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall());
 
   const HBasicBlock* const block = instruction->GetBlock();
   const HLoopInformation* const info = block->GetLoopInformation();
@@ -2925,16 +2926,20 @@
 
   // Convert the jumps into the result.
   vixl32::Label done_label;
+  vixl32::Label* final_label = codegen_->GetFinalLabel(cond, &done_label);
 
   // False case: result = 0.
   __ Bind(&false_label);
   __ Mov(out, 0);
-  __ B(&done_label);
+  __ B(final_label);
 
   // True case: result = 1.
   __ Bind(&true_label);
   __ Mov(out, 1);
-  __ Bind(&done_label);
+
+  if (done_label.IsReferenced()) {
+    __ Bind(&done_label);
+  }
 }
 
 void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
@@ -4447,6 +4452,7 @@
     vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
     vixl32::Label end;
     vixl32::Label shift_by_32_plus_shift_right;
+    vixl32::Label* final_label = codegen_->GetFinalLabel(ror, &end);
 
     __ And(shift_right, RegisterFrom(rhs), 0x1F);
     __ Lsrs(shift_left, RegisterFrom(rhs), 6);
@@ -4461,7 +4467,7 @@
     __ Lsl(out_reg_lo, in_reg_lo, shift_left);
     __ Lsr(shift_left, in_reg_hi, shift_right);
     __ Add(out_reg_lo, out_reg_lo, shift_left);
-    __ B(&end);
+    __ B(final_label);
 
     __ Bind(&shift_by_32_plus_shift_right);  // Shift by 32+shift_right.
     // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
@@ -4473,7 +4479,9 @@
     __ Lsl(shift_right, in_reg_hi, shift_left);
     __ Add(out_reg_lo, out_reg_lo, shift_right);
 
-    __ Bind(&end);
+    if (end.IsReferenced()) {
+      __ Bind(&end);
+    }
   }
 }
 
@@ -4906,6 +4914,7 @@
   Location right = locations->InAt(1);
 
   vixl32::Label less, greater, done;
+  vixl32::Label* final_label = codegen_->GetFinalLabel(compare, &done);
   Primitive::Type type = compare->InputAt(0)->GetType();
   vixl32::Condition less_cond = vixl32::Condition(kNone);
   switch (type) {
@@ -4944,17 +4953,19 @@
       UNREACHABLE();
   }
 
-  __ B(eq, &done, /* far_target */ false);
+  __ B(eq, final_label, /* far_target */ false);
   __ B(less_cond, &less, /* far_target */ false);
 
   __ Bind(&greater);
   __ Mov(out, 1);
-  __ B(&done);
+  __ B(final_label);
 
   __ Bind(&less);
   __ Mov(out, -1);
 
-  __ Bind(&done);
+  if (done.IsReferenced()) {
+    __ Bind(&done);
+  }
 }
 
 void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
@@ -5746,6 +5757,7 @@
         int32_t const_index = Int32ConstantFrom(index);
         if (maybe_compressed_char_at) {
           vixl32::Label uncompressed_load, done;
+          vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
           __ Lsrs(length, length, 1u);  // LSRS has a 16-bit encoding, TST (immediate) does not.
           static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
                         "Expecting 0=compressed, 1=uncompressed");
@@ -5754,13 +5766,15 @@
                                          RegisterFrom(out_loc),
                                          obj,
                                          data_offset + const_index);
-          __ B(&done);
+          __ B(final_label);
           __ Bind(&uncompressed_load);
           GetAssembler()->LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar),
                                          RegisterFrom(out_loc),
                                          obj,
                                          data_offset + (const_index << 1));
-          __ Bind(&done);
+          if (done.IsReferenced()) {
+            __ Bind(&done);
+          }
         } else {
           uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
 
@@ -5785,15 +5799,18 @@
         }
         if (maybe_compressed_char_at) {
           vixl32::Label uncompressed_load, done;
+          vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
           __ Lsrs(length, length, 1u);  // LSRS has a 16-bit encoding, TST (immediate) does not.
           static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
                         "Expecting 0=compressed, 1=uncompressed");
           __ B(cs, &uncompressed_load, /* far_target */ false);
           __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
-          __ B(&done);
+          __ B(final_label);
           __ Bind(&uncompressed_load);
           __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
-          __ Bind(&done);
+          if (done.IsReferenced()) {
+            __ Bind(&done);
+          }
         } else {
           codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
         }
@@ -6032,6 +6049,7 @@
       uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
       uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
       vixl32::Label done;
+      vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
       SlowPathCodeARMVIXL* slow_path = nullptr;
 
       if (may_need_runtime_call_for_type_check) {
@@ -6054,7 +6072,7 @@
           // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
           // store instruction.
           codegen_->MaybeRecordImplicitNullCheck(instruction);
-          __ B(&done);
+          __ B(final_label);
           __ Bind(&non_zero);
         }
 
@@ -7062,6 +7080,7 @@
   uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
   uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
   vixl32::Label done, zero;
+  vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
   SlowPathCodeARMVIXL* slow_path = nullptr;
 
   // Return 0 if `obj` is null.
@@ -7083,7 +7102,7 @@
       // Classes must be equal for the instanceof to succeed.
       __ B(ne, &zero, /* far_target */ false);
       __ Mov(out, 1);
-      __ B(&done);
+      __ B(final_label);
       break;
     }
 
@@ -7106,12 +7125,12 @@
                                        maybe_temp_loc,
                                        kCompilerReadBarrierOption);
       // If `out` is null, we use it for the result, and jump to `done`.
-      __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
+      __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
       __ Cmp(out, cls);
       __ B(ne, &loop, /* far_target */ false);
       __ Mov(out, 1);
       if (zero.IsReferenced()) {
-        __ B(&done);
+        __ B(final_label);
       }
       break;
     }
@@ -7137,11 +7156,11 @@
                                        kCompilerReadBarrierOption);
       __ CompareAndBranchIfNonZero(out, &loop);
       // If `out` is null, we use it for the result, and jump to `done`.
-      __ B(&done);
+      __ B(final_label);
       __ Bind(&success);
       __ Mov(out, 1);
       if (zero.IsReferenced()) {
-        __ B(&done);
+        __ B(final_label);
       }
       break;
     }
@@ -7166,13 +7185,13 @@
                                        maybe_temp_loc,
                                        kCompilerReadBarrierOption);
       // If `out` is null, we use it for the result, and jump to `done`.
-      __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
+      __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
       GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
       __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false);
       __ Bind(&exact_check);
       __ Mov(out, 1);
-      __ B(&done);
+      __ B(final_label);
       break;
     }
 
@@ -7193,7 +7212,7 @@
       __ B(ne, slow_path->GetEntryLabel());
       __ Mov(out, 1);
       if (zero.IsReferenced()) {
-        __ B(&done);
+        __ B(final_label);
       }
       break;
     }
@@ -7224,7 +7243,7 @@
       codegen_->AddSlowPath(slow_path);
       __ B(slow_path->GetEntryLabel());
       if (zero.IsReferenced()) {
-        __ B(&done);
+        __ B(final_label);
       }
       break;
     }
@@ -7310,9 +7329,10 @@
   codegen_->AddSlowPath(type_check_slow_path);
 
   vixl32::Label done;
+  vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
   // Avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
-    __ CompareAndBranchIfZero(obj, &done, /* far_target */ false);
+    __ CompareAndBranchIfZero(obj, final_label, /* far_target */ false);
   }
 
   switch (type_check_kind) {
@@ -7376,7 +7396,7 @@
       vixl32::Label loop;
       __ Bind(&loop);
       __ Cmp(temp, cls);
-      __ B(eq, &done, /* far_target */ false);
+      __ B(eq, final_label, /* far_target */ false);
 
       // /* HeapReference<Class> */ temp = temp->super_class_
       GenerateReferenceLoadOneRegister(instruction,
@@ -7404,7 +7424,7 @@
 
       // Do an exact check.
       __ Cmp(temp, cls);
-      __ B(eq, &done, /* far_target */ false);
+      __ B(eq, final_label, /* far_target */ false);
 
       // Otherwise, we need to check that the object's class is a non-primitive array.
       // /* HeapReference<Class> */ temp = temp->component_type_
@@ -7472,7 +7492,9 @@
       break;
     }
   }
-  __ Bind(&done);
+  if (done.IsReferenced()) {
+    __ Bind(&done);
+  }
 
   __ Bind(type_check_slow_path->GetExitLabel());
 }
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 8a813bd..62f5114 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -55,6 +55,9 @@
 // Avoid inlining within a huge method due to memory pressure.
 static constexpr size_t kMaximumCodeUnitSize = 4096;
 
+// Controls the use of inline caches in AOT mode.
+static constexpr bool kUseAOTInlineCaches = false;
+
 void HInliner::Run() {
   const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions();
   if ((compiler_options.GetInlineDepthLimit() == 0)
@@ -376,6 +379,10 @@
                                         HInvoke* invoke_instruction,
                                         ArtMethod* resolved_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (Runtime::Current()->IsAotCompiler() && !kUseAOTInlineCaches) {
+    return false;
+  }
+
   StackHandleScope<1> hs(Thread::Current());
   Handle<mirror::ObjectArray<mirror::Class>> inline_cache;
   InlineCacheType inline_cache_type = Runtime::Current()->IsAotCompiler()
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 98b80f5..1006a77 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -270,9 +270,11 @@
   locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
 }
 
-static void GenNumberOfLeadingZeros(LocationSummary* locations,
+static void GenNumberOfLeadingZeros(HInvoke* invoke,
                                     Primitive::Type type,
-                                    ArmAssembler* assembler) {
+                                    CodeGeneratorARM* codegen) {
+  ArmAssembler* assembler = codegen->GetAssembler();
+  LocationSummary* locations = invoke->GetLocations();
   Location in = locations->InAt(0);
   Register out = locations->Out().AsRegister<Register>();
 
@@ -282,11 +284,14 @@
     Register in_reg_lo = in.AsRegisterPairLow<Register>();
     Register in_reg_hi = in.AsRegisterPairHigh<Register>();
     Label end;
+    Label* final_label = codegen->GetFinalLabel(invoke, &end);
     __ clz(out, in_reg_hi);
-    __ CompareAndBranchIfNonZero(in_reg_hi, &end);
+    __ CompareAndBranchIfNonZero(in_reg_hi, final_label);
     __ clz(out, in_reg_lo);
     __ AddConstant(out, 32);
-    __ Bind(&end);
+    if (end.IsLinked()) {
+      __ Bind(&end);
+    }
   } else {
     __ clz(out, in.AsRegister<Register>());
   }
@@ -297,7 +302,7 @@
 }
 
 void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
-  GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+  GenNumberOfLeadingZeros(invoke, Primitive::kPrimInt, codegen_);
 }
 
 void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
@@ -309,27 +314,32 @@
 }
 
 void IntrinsicCodeGeneratorARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
-  GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+  GenNumberOfLeadingZeros(invoke, Primitive::kPrimLong, codegen_);
 }
 
-static void GenNumberOfTrailingZeros(LocationSummary* locations,
+static void GenNumberOfTrailingZeros(HInvoke* invoke,
                                      Primitive::Type type,
-                                     ArmAssembler* assembler) {
+                                     CodeGeneratorARM* codegen) {
   DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
 
+  ArmAssembler* assembler = codegen->GetAssembler();
+  LocationSummary* locations = invoke->GetLocations();
   Register out = locations->Out().AsRegister<Register>();
 
   if (type == Primitive::kPrimLong) {
     Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
     Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
     Label end;
+    Label* final_label = codegen->GetFinalLabel(invoke, &end);
     __ rbit(out, in_reg_lo);
     __ clz(out, out);
-    __ CompareAndBranchIfNonZero(in_reg_lo, &end);
+    __ CompareAndBranchIfNonZero(in_reg_lo, final_label);
     __ rbit(out, in_reg_hi);
     __ clz(out, out);
     __ AddConstant(out, 32);
-    __ Bind(&end);
+    if (end.IsLinked()) {
+      __ Bind(&end);
+    }
   } else {
     Register in = locations->InAt(0).AsRegister<Register>();
     __ rbit(out, in);
@@ -346,7 +356,7 @@
 }
 
 void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
-  GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+  GenNumberOfTrailingZeros(invoke, Primitive::kPrimInt, codegen_);
 }
 
 void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
@@ -358,7 +368,7 @@
 }
 
 void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
-  GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+  GenNumberOfTrailingZeros(invoke, Primitive::kPrimLong, codegen_);
 }
 
 static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
@@ -1355,6 +1365,7 @@
   Label end;
   Label return_true;
   Label return_false;
+  Label* final_label = codegen_->GetFinalLabel(invoke, &end);
 
   // Get offsets of count, value, and class fields within a string object.
   const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
@@ -1428,12 +1439,15 @@
   // If loop does not result in returning false, we return true.
   __ Bind(&return_true);
   __ LoadImmediate(out, 1);
-  __ b(&end);
+  __ b(final_label);
 
   // Return false and exit the function.
   __ Bind(&return_false);
   __ LoadImmediate(out, 0);
-  __ Bind(&end);
+
+  if (end.IsLinked()) {
+    __ Bind(&end);
+  }
 }
 
 static void GenerateVisitStringIndexOf(HInvoke* invoke,
@@ -2491,13 +2505,14 @@
   Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
 
   Label done, compressed_string_loop;
+  Label* final_label = codegen_->GetFinalLabel(invoke, &done);
   // dst to be copied.
   __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
   __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
 
   __ subs(num_chr, srcEnd, ShifterOperand(srcBegin));
   // Early out for valid zero-length retrievals.
-  __ b(&done, EQ);
+  __ b(final_label, EQ);
 
   // src range to copy.
   __ add(src_ptr, srcObj, ShifterOperand(value_offset));
@@ -2534,7 +2549,7 @@
   __ b(&loop, GE);
 
   __ adds(num_chr, num_chr, ShifterOperand(4));
-  __ b(&done, EQ);
+  __ b(final_label, EQ);
 
   // Main loop for < 4 character case and remainder handling. Loads and stores one
   // 16-bit Java character at a time.
@@ -2545,7 +2560,7 @@
   __ b(&remainder, GT);
 
   if (mirror::kUseStringCompression) {
-    __ b(&done);
+    __ b(final_label);
 
     const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
     DCHECK_EQ(c_char_size, 1u);
@@ -2559,7 +2574,9 @@
     __ b(&compressed_string_loop, GT);
   }
 
-  __ Bind(&done);
+  if (done.IsLinked()) {
+    __ Bind(&done);
+  }
 }
 
 void IntrinsicLocationsBuilderARM::VisitFloatIsInfinite(HInvoke* invoke) {
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 19ff49c..b25bad7 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -333,9 +333,11 @@
   locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
 }
 
-static void GenNumberOfLeadingZeros(LocationSummary* locations,
+static void GenNumberOfLeadingZeros(HInvoke* invoke,
                                     Primitive::Type type,
-                                    ArmVIXLAssembler* assembler) {
+                                    CodeGeneratorARMVIXL* codegen) {
+  ArmVIXLAssembler* assembler = codegen->GetAssembler();
+  LocationSummary* locations = invoke->GetLocations();
   Location in = locations->InAt(0);
   vixl32::Register out = RegisterFrom(locations->Out());
 
@@ -345,11 +347,14 @@
     vixl32::Register in_reg_lo = LowRegisterFrom(in);
     vixl32::Register in_reg_hi = HighRegisterFrom(in);
     vixl32::Label end;
+    vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &end);
     __ Clz(out, in_reg_hi);
-    __ CompareAndBranchIfNonZero(in_reg_hi, &end, /* far_target */ false);
+    __ CompareAndBranchIfNonZero(in_reg_hi, final_label, /* far_target */ false);
     __ Clz(out, in_reg_lo);
     __ Add(out, out, 32);
-    __ Bind(&end);
+    if (end.IsReferenced()) {
+      __ Bind(&end);
+    }
   } else {
     __ Clz(out, RegisterFrom(in));
   }
@@ -360,7 +365,7 @@
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
-  GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+  GenNumberOfLeadingZeros(invoke, Primitive::kPrimInt, codegen_);
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
@@ -372,27 +377,32 @@
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
-  GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+  GenNumberOfLeadingZeros(invoke, Primitive::kPrimLong, codegen_);
 }
 
-static void GenNumberOfTrailingZeros(LocationSummary* locations,
+static void GenNumberOfTrailingZeros(HInvoke* invoke,
                                      Primitive::Type type,
-                                     ArmVIXLAssembler* assembler) {
+                                     CodeGeneratorARMVIXL* codegen) {
   DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
 
+  ArmVIXLAssembler* assembler = codegen->GetAssembler();
+  LocationSummary* locations = invoke->GetLocations();
   vixl32::Register out = RegisterFrom(locations->Out());
 
   if (type == Primitive::kPrimLong) {
     vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
     vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
     vixl32::Label end;
+    vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &end);
     __ Rbit(out, in_reg_lo);
     __ Clz(out, out);
-    __ CompareAndBranchIfNonZero(in_reg_lo, &end, /* far_target */ false);
+    __ CompareAndBranchIfNonZero(in_reg_lo, final_label, /* far_target */ false);
     __ Rbit(out, in_reg_hi);
     __ Clz(out, out);
     __ Add(out, out, 32);
-    __ Bind(&end);
+    if (end.IsReferenced()) {
+      __ Bind(&end);
+    }
   } else {
     vixl32::Register in = RegisterFrom(locations->InAt(0));
     __ Rbit(out, in);
@@ -409,7 +419,7 @@
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
-  GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+  GenNumberOfTrailingZeros(invoke, Primitive::kPrimInt, codegen_);
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
@@ -421,7 +431,7 @@
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
-  GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+  GenNumberOfTrailingZeros(invoke, Primitive::kPrimLong, codegen_);
 }
 
 static void MathAbsFP(HInvoke* invoke, ArmVIXLAssembler* assembler) {
@@ -502,7 +512,8 @@
   GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
 }
 
-static void GenMinMaxFloat(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) {
+static void GenMinMaxFloat(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) {
+  ArmVIXLAssembler* assembler = codegen->GetAssembler();
   Location op1_loc = invoke->GetLocations()->InAt(0);
   Location op2_loc = invoke->GetLocations()->InAt(1);
   Location out_loc = invoke->GetLocations()->Out();
@@ -520,6 +531,7 @@
   const vixl32::Register temp1 = temps.Acquire();
   vixl32::Register temp2 = RegisterFrom(invoke->GetLocations()->GetTemp(0));
   vixl32::Label nan, done;
+  vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &done);
 
   DCHECK(op1.Is(out));
 
@@ -536,7 +548,8 @@
     __ it(cond);
     __ vmov(cond, F32, out, op2);
   }
-  __ B(ne, &done, /* far_target */ false);  // for <>(not equal), we've done min/max calculation.
+  // for <>(not equal), we've done min/max calculation.
+  __ B(ne, final_label, /* far_target */ false);
 
   // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0).
   __ Vmov(temp1, op1);
@@ -547,14 +560,16 @@
     __ And(temp1, temp1, temp2);
   }
   __ Vmov(out, temp1);
-  __ B(&done);
+  __ B(final_label);
 
   // handle NaN input.
   __ Bind(&nan);
   __ Movt(temp1, High16Bits(kNanFloat));  // 0x7FC0xxxx is a NaN.
   __ Vmov(out, temp1);
 
-  __ Bind(&done);
+  if (done.IsReferenced()) {
+    __ Bind(&done);
+  }
 }
 
 static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
@@ -572,7 +587,7 @@
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitMathMinFloatFloat(HInvoke* invoke) {
-  GenMinMaxFloat(invoke, /* is_min */ true, GetAssembler());
+  GenMinMaxFloat(invoke, /* is_min */ true, codegen_);
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) {
@@ -581,10 +596,11 @@
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  GenMinMaxFloat(invoke, /* is_min */ false, GetAssembler());
+  GenMinMaxFloat(invoke, /* is_min */ false, codegen_);
 }
 
-static void GenMinMaxDouble(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) {
+static void GenMinMaxDouble(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) {
+  ArmVIXLAssembler* assembler = codegen->GetAssembler();
   Location op1_loc = invoke->GetLocations()->InAt(0);
   Location op2_loc = invoke->GetLocations()->InAt(1);
   Location out_loc = invoke->GetLocations()->Out();
@@ -599,6 +615,7 @@
   vixl32::DRegister op2 = DRegisterFrom(op2_loc);
   vixl32::DRegister out = OutputDRegister(invoke);
   vixl32::Label handle_nan_eq, done;
+  vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &done);
 
   DCHECK(op1.Is(out));
 
@@ -615,19 +632,22 @@
     __ it(cond);
     __ vmov(cond, F64, out, op2);
   }
-  __ B(ne, &done, /* far_target */ false);  // for <>(not equal), we've done min/max calculation.
+  // for <>(not equal), we've done min/max calculation.
+  __ B(ne, final_label, /* far_target */ false);
 
   // handle op1 == op2, max(+0.0,-0.0).
   if (!is_min) {
     __ Vand(F64, out, op1, op2);
-    __ B(&done);
+    __ B(final_label);
   }
 
   // handle op1 == op2, min(+0.0,-0.0), NaN input.
   __ Bind(&handle_nan_eq);
   __ Vorr(F64, out, op1, op2);  // assemble op1/-0.0/NaN.
 
-  __ Bind(&done);
+  if (done.IsReferenced()) {
+    __ Bind(&done);
+  }
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) {
@@ -635,7 +655,7 @@
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  GenMinMaxDouble(invoke, /* is_min */ true , GetAssembler());
+  GenMinMaxDouble(invoke, /* is_min */ true , codegen_);
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) {
@@ -643,7 +663,7 @@
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  GenMinMaxDouble(invoke, /* is_min */ false, GetAssembler());
+  GenMinMaxDouble(invoke, /* is_min */ false, codegen_);
 }
 
 static void GenMinMaxLong(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) {
@@ -1670,6 +1690,7 @@
   vixl32::Label end;
   vixl32::Label return_true;
   vixl32::Label return_false;
+  vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &end);
 
   // Get offsets of count, value, and class fields within a string object.
   const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
@@ -1746,12 +1767,15 @@
   // If loop does not result in returning false, we return true.
   __ Bind(&return_true);
   __ Mov(out, 1);
-  __ B(&end);
+  __ B(final_label);
 
   // Return false and exit the function.
   __ Bind(&return_false);
   __ Mov(out, 0);
-  __ Bind(&end);
+
+  if (end.IsReferenced()) {
+    __ Bind(&end);
+  }
 }
 
 static void GenerateVisitStringIndexOf(HInvoke* invoke,
@@ -2789,13 +2813,14 @@
   vixl32::Register dst_ptr = RegisterFrom(locations->GetTemp(2));
 
   vixl32::Label done, compressed_string_loop;
+  vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &done);
   // dst to be copied.
   __ Add(dst_ptr, dstObj, data_offset);
   __ Add(dst_ptr, dst_ptr, Operand(dstBegin, vixl32::LSL, 1));
 
   __ Subs(num_chr, srcEnd, srcBegin);
   // Early out for valid zero-length retrievals.
-  __ B(eq, &done, /* far_target */ false);
+  __ B(eq, final_label, /* far_target */ false);
 
   // src range to copy.
   __ Add(src_ptr, srcObj, value_offset);
@@ -2839,7 +2864,7 @@
   __ B(ge, &loop, /* far_target */ false);
 
   __ Adds(num_chr, num_chr, 4);
-  __ B(eq, &done, /* far_target */ false);
+  __ B(eq, final_label, /* far_target */ false);
 
   // Main loop for < 4 character case and remainder handling. Loads and stores one
   // 16-bit Java character at a time.
@@ -2852,7 +2877,7 @@
   __ B(gt, &remainder, /* far_target */ false);
 
   if (mirror::kUseStringCompression) {
-    __ B(&done);
+    __ B(final_label);
 
     const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
     DCHECK_EQ(c_char_size, 1u);
@@ -2868,7 +2893,9 @@
     __ B(gt, &compressed_string_loop, /* far_target */ false);
   }
 
-  __ Bind(&done);
+  if (done.IsReferenced()) {
+    __ Bind(&done);
+  }
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 0924aec..048f36d 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -14,6 +14,12 @@
 // limitations under the License.
 //
 
+cc_library_headers {
+    name: "dex2oat_headers",
+    host_supported: true,
+    export_include_dirs: ["include"],
+}
+
 cc_defaults {
     name: "dex2oat-defaults",
     host_supported: true,
@@ -40,6 +46,7 @@
     include_dirs: [
         "art/cmdline",
     ],
+    header_libs: ["dex2oat_headers"],
 }
 
 art_cc_binary {
@@ -132,4 +139,5 @@
         "art_gtest_defaults",
     ],
     srcs: ["dex2oat_test.cc"],
+    header_libs: ["dex2oat_headers"],
 }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 92a12c8..e80be81 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -54,6 +54,7 @@
 #include "debug/method_debug_info.h"
 #include "dex/quick_compiler_callbacks.h"
 #include "dex/verification_results.h"
+#include "dex2oat_return_codes.h"
 #include "dex_file-inl.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
@@ -1442,11 +1443,11 @@
 
   // Set up the environment for compilation. Includes starting the runtime and loading/opening the
   // boot class path.
-  bool Setup() {
+  dex2oat::ReturnCode Setup() {
     TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
 
     if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
-      return false;
+      return dex2oat::ReturnCode::kOther;
     }
 
     verification_results_.reset(new VerificationResults(compiler_options_.get()));
@@ -1458,12 +1459,12 @@
 
     RuntimeArgumentMap runtime_options;
     if (!PrepareRuntimeOptions(&runtime_options)) {
-      return false;
+      return dex2oat::ReturnCode::kOther;
     }
 
     CreateOatWriters();
     if (!AddDexFileSources()) {
-      return false;
+      return dex2oat::ReturnCode::kOther;
     }
 
     if (IsBootImage() && image_filenames_.size() > 1) {
@@ -1479,7 +1480,7 @@
       // When compiling an app, create the runtime early to retrieve
       // the image location key needed for the oat header.
       if (!CreateRuntime(std::move(runtime_options))) {
-        return false;
+        return dex2oat::ReturnCode::kCreateRuntime;
       }
 
       if (CompilerFilter::DependsOnImageChecksum(compiler_options_->GetCompilerFilter())) {
@@ -1550,7 +1551,7 @@
             update_input_vdex_,
             &opened_dex_files_map,
             &opened_dex_files)) {
-          return false;
+          return dex2oat::ReturnCode::kOther;
         }
         dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files));
         if (opened_dex_files_map != nullptr) {
@@ -1602,7 +1603,7 @@
       // Note: Runtime acquires ownership of these dex files.
       runtime_options.Set(RuntimeArgumentMap::BootClassPathDexList, &opened_dex_files_);
       if (!CreateRuntime(std::move(runtime_options))) {
-        return false;
+        return dex2oat::ReturnCode::kOther;
       }
     }
 
@@ -1636,7 +1637,7 @@
     for (const std::unique_ptr<MemMap>& map : opened_dex_files_maps_) {
       if (!map->Protect(PROT_READ | PROT_WRITE)) {
         PLOG(ERROR) << "Failed to make .dex files writeable.";
-        return false;
+        return dex2oat::ReturnCode::kOther;
       }
     }
 
@@ -1651,14 +1652,14 @@
         soa.Self()->AssertPendingException();
         soa.Self()->ClearException();
         PLOG(ERROR) << "Failed to register dex file.";
-        return false;
+        return dex2oat::ReturnCode::kOther;
       }
       // Pre-register dex files so that we can access verification results without locks during
       // compilation and verification.
       verification_results_->AddDexFile(dex_file);
     }
 
-    return true;
+    return dex2oat::ReturnCode::kNoFailure;
   }
 
   // If we need to keep the oat file open for the image writer.
@@ -2789,13 +2790,13 @@
 #endif
 }
 
-static int CompileImage(Dex2Oat& dex2oat) {
+static dex2oat::ReturnCode CompileImage(Dex2Oat& dex2oat) {
   dex2oat.LoadClassProfileDescriptors();
   dex2oat.Compile();
 
   if (!dex2oat.WriteOutputFiles()) {
     dex2oat.EraseOutputFiles();
-    return EXIT_FAILURE;
+    return dex2oat::ReturnCode::kOther;
   }
 
   // Flush boot.oat. We always expect the output file by name, and it will be re-opened from the
@@ -2804,46 +2805,46 @@
   if (dex2oat.ShouldKeepOatFileOpen()) {
     if (!dex2oat.FlushOutputFiles()) {
       dex2oat.EraseOutputFiles();
-      return EXIT_FAILURE;
+      return dex2oat::ReturnCode::kOther;
     }
   } else if (!dex2oat.FlushCloseOutputFiles()) {
-    return EXIT_FAILURE;
+    return dex2oat::ReturnCode::kOther;
   }
 
   // Creates the boot.art and patches the oat files.
   if (!dex2oat.HandleImage()) {
-    return EXIT_FAILURE;
+    return dex2oat::ReturnCode::kOther;
   }
 
   // When given --host, finish early without stripping.
   if (dex2oat.IsHost()) {
     if (!dex2oat.FlushCloseOutputFiles()) {
-      return EXIT_FAILURE;
+      return dex2oat::ReturnCode::kOther;
     }
     dex2oat.DumpTiming();
-    return EXIT_SUCCESS;
+    return dex2oat::ReturnCode::kNoFailure;
   }
 
   // Copy stripped to unstripped location, if necessary.
   if (!dex2oat.CopyStrippedToUnstripped()) {
-    return EXIT_FAILURE;
+    return dex2oat::ReturnCode::kOther;
   }
 
   // FlushClose again, as stripping might have re-opened the oat files.
   if (!dex2oat.FlushCloseOutputFiles()) {
-    return EXIT_FAILURE;
+    return dex2oat::ReturnCode::kOther;
   }
 
   dex2oat.DumpTiming();
-  return EXIT_SUCCESS;
+  return dex2oat::ReturnCode::kNoFailure;
 }
 
-static int CompileApp(Dex2Oat& dex2oat) {
+static dex2oat::ReturnCode CompileApp(Dex2Oat& dex2oat) {
   dex2oat.Compile();
 
   if (!dex2oat.WriteOutputFiles()) {
     dex2oat.EraseOutputFiles();
-    return EXIT_FAILURE;
+    return dex2oat::ReturnCode::kOther;
   }
 
   // Do not close the oat files here. We might have gotten the output file by file descriptor,
@@ -2852,29 +2853,29 @@
   // When given --host, finish early without stripping.
   if (dex2oat.IsHost()) {
     if (!dex2oat.FlushCloseOutputFiles()) {
-      return EXIT_FAILURE;
+      return dex2oat::ReturnCode::kOther;
     }
 
     dex2oat.DumpTiming();
-    return EXIT_SUCCESS;
+    return dex2oat::ReturnCode::kNoFailure;
   }
 
   // Copy stripped to unstripped location, if necessary. This will implicitly flush & close the
   // stripped versions. If this is given, we expect to be able to open writable files by name.
   if (!dex2oat.CopyStrippedToUnstripped()) {
-    return EXIT_FAILURE;
+    return dex2oat::ReturnCode::kOther;
   }
 
   // Flush and close the files.
   if (!dex2oat.FlushCloseOutputFiles()) {
-    return EXIT_FAILURE;
+    return dex2oat::ReturnCode::kOther;
   }
 
   dex2oat.DumpTiming();
-  return EXIT_SUCCESS;
+  return dex2oat::ReturnCode::kNoFailure;
 }
 
-static int dex2oat(int argc, char** argv) {
+static dex2oat::ReturnCode Dex2oat(int argc, char** argv) {
   b13564922();
 
   TimingLogger timings("compiler", false, false);
@@ -2893,14 +2894,14 @@
   if (dex2oat->UseProfile()) {
     if (!dex2oat->LoadProfile()) {
       LOG(ERROR) << "Failed to process profile file";
-      return EXIT_FAILURE;
+      return dex2oat::ReturnCode::kOther;
     }
   }
 
   if (dex2oat->DoDexLayoutOptimizations()) {
     if (dex2oat->HasInputVdexFile()) {
       LOG(ERROR) << "Dexlayout is incompatible with an input VDEX";
-      return EXIT_FAILURE;
+      return dex2oat::ReturnCode::kOther;
     }
   }
 
@@ -2908,7 +2909,7 @@
 
   // Check early that the result of compilation can be written
   if (!dex2oat->OpenFile()) {
-    return EXIT_FAILURE;
+    return dex2oat::ReturnCode::kOther;
   }
 
   // Print the complete line when any of the following is true:
@@ -2923,16 +2924,17 @@
     LOG(INFO) << StrippedCommandLine();
   }
 
-  if (!dex2oat->Setup()) {
+  dex2oat::ReturnCode setup_code = dex2oat->Setup();
+  if (setup_code != dex2oat::ReturnCode::kNoFailure) {
     dex2oat->EraseOutputFiles();
-    return EXIT_FAILURE;
+    return setup_code;
   }
 
   // Helps debugging on device. Can be used to determine which dalvikvm instance invoked a dex2oat
   // instance. Used by tools/bisection_search/bisection_search.py.
   VLOG(compiler) << "Running dex2oat (parent PID = " << getppid() << ")";
 
-  bool result;
+  dex2oat::ReturnCode result;
   if (dex2oat->IsImage()) {
     result = CompileImage(*dex2oat);
   } else {
@@ -2945,7 +2947,7 @@
 }  // namespace art
 
 int main(int argc, char** argv) {
-  int result = art::dex2oat(argc, argv);
+  int result = static_cast<int>(art::Dex2oat(argc, argv));
   // Everything was done, 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. Note: The Dex2Oat class
   // should not destruct the runtime in this case.
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 289b8ab..8c14b50 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -30,6 +30,7 @@
 #include "base/macros.h"
 #include "dex_file-inl.h"
 #include "dex2oat_environment_test.h"
+#include "dex2oat_return_codes.h"
 #include "jit/profile_compilation_info.h"
 #include "oat.h"
 #include "oat_file.h"
@@ -50,12 +51,12 @@
   }
 
  protected:
-  void GenerateOdexForTest(const std::string& dex_location,
-                           const std::string& odex_location,
-                           CompilerFilter::Filter filter,
-                           const std::vector<std::string>& extra_args = {},
-                           bool expect_success = true,
-                           bool use_fd = false) {
+  int GenerateOdexForTestWithStatus(const std::string& dex_location,
+                                    const std::string& odex_location,
+                                    CompilerFilter::Filter filter,
+                                    std::string* error_msg,
+                                    const std::vector<std::string>& extra_args = {},
+                                    bool use_fd = false) {
     std::unique_ptr<File> oat_file;
     std::vector<std::string> args;
     args.push_back("--dex-file=" + dex_location);
@@ -73,12 +74,27 @@
 
     args.insert(args.end(), extra_args.begin(), extra_args.end());
 
-    std::string error_msg;
-    bool success = Dex2Oat(args, &error_msg);
+    int status = Dex2Oat(args, error_msg);
     if (oat_file != nullptr) {
-      ASSERT_EQ(oat_file->FlushClose(), 0) << "Could not flush and close oat file";
+      CHECK_EQ(oat_file->FlushClose(), 0) << "Could not flush and close oat file";
     }
+    return status;
+  }
 
+  void GenerateOdexForTest(const std::string& dex_location,
+                           const std::string& odex_location,
+                           CompilerFilter::Filter filter,
+                           const std::vector<std::string>& extra_args = {},
+                           bool expect_success = true,
+                           bool use_fd = false) {
+    std::string error_msg;
+    int status = GenerateOdexForTestWithStatus(dex_location,
+                                               odex_location,
+                                               filter,
+                                               &error_msg,
+                                               extra_args,
+                                               use_fd);
+    bool success = (status == 0);
     if (expect_success) {
       ASSERT_TRUE(success) << error_msg << std::endl << output_;
 
@@ -118,7 +134,7 @@
     EXPECT_EQ(expected, actual);
   }
 
-  bool Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) {
+  int Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) {
     Runtime* runtime = Runtime::Current();
 
     const std::vector<gc::space::ImageSpace*>& image_spaces =
@@ -196,6 +212,7 @@
       c_args.push_back(nullptr);
       execv(c_args[0], const_cast<char* const*>(c_args.data()));
       exit(1);
+      UNREACHABLE();
     } else {
       close(link[1]);
       char buffer[128];
@@ -206,12 +223,12 @@
         output_ += std::string(buffer, bytes_read);
       }
       close(link[0]);
-      int status = 0;
+      int status = -1;
       if (waitpid(pid, &status, 0) != -1) {
         success_ = (status == 0);
       }
+      return status;
     }
-    return success_;
   }
 
   std::string output_ = "";
@@ -845,4 +862,30 @@
   RunTest(false, { "--watchdog-timeout=10" });
 }
 
+class Dex2oatReturnCodeTest : public Dex2oatTest {
+ protected:
+  int RunTest(const std::vector<std::string>& extra_args = {}) {
+    std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
+    std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
+
+    Copy(GetTestDexFileName(), dex_location);
+
+    std::string error_msg;
+    return GenerateOdexForTestWithStatus(dex_location,
+                                         odex_location,
+                                         CompilerFilter::kSpeed,
+                                         &error_msg,
+                                         extra_args);
+  }
+
+  std::string GetTestDexFileName() {
+    return GetDexSrc1();
+  }
+};
+
+TEST_F(Dex2oatReturnCodeTest, TestCreateRuntime) {
+  int status = RunTest({ "--boot-image=/this/does/not/exist/yolo.oat" });
+  EXPECT_EQ(static_cast<int>(dex2oat::ReturnCode::kCreateRuntime), WEXITSTATUS(status)) << output_;
+}
+
 }  // namespace art
diff --git a/dex2oat/include/dex2oat_return_codes.h b/dex2oat/include/dex2oat_return_codes.h
new file mode 100644
index 0000000..cc5400f
--- /dev/null
+++ b/dex2oat/include/dex2oat_return_codes.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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_DEX2OAT_INCLUDE_DEX2OAT_RETURN_CODES_H_
+#define ART_DEX2OAT_INCLUDE_DEX2OAT_RETURN_CODES_H_
+
+namespace art {
+namespace dex2oat {
+
+enum class ReturnCode : int {
+  kNoFailure = 0,
+  kOther = 1,
+  kCreateRuntime = 2,
+};
+
+}  // namespace dex2oat
+}  // namespace art
+
+#endif  // ART_DEX2OAT_INCLUDE_DEX2OAT_RETURN_CODES_H_
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 52f3b52..1a8a614 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -22,6 +22,7 @@
 #include "exec_utils.h"
 #include "jit/profile_compilation_info.h"
 #include "mirror/class-inl.h"
+#include "obj_ptr-inl.h"
 #include "profile_assistant.h"
 #include "scoped_thread_state_change-inl.h"
 #include "utils.h"
@@ -140,7 +141,8 @@
     return true;
   }
 
-  bool CreateAndDump(const std::string& input_file_contents, std::string* output_file_contents) {
+  bool CreateAndDump(const std::string& input_file_contents,
+                     std::string* output_file_contents) {
     ScratchFile profile_file;
     EXPECT_TRUE(CreateProfile(input_file_contents,
                               profile_file.GetFilename(),
@@ -156,7 +158,7 @@
     ScopedObjectAccess soa(self);
     StackHandleScope<1> hs(self);
     Handle<mirror::ClassLoader> h_loader(
-        hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader()));
+        hs.NewHandle(ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(class_loader))));
     return class_linker->FindClass(self, clazz.c_str(), h_loader);
   }
 
@@ -442,6 +444,44 @@
   ASSERT_EQ(output_file_contents, expected_contents);
 }
 
+TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) {
+  // Class names put here need to be in sorted order.
+  std::vector<std::string> class_names = {
+    "Ljava/lang/Math;->*",
+  };
+  std::string input_file_contents;
+  std::string expected_contents;
+  for (std::string& class_name : class_names) {
+    input_file_contents += class_name + std::string("\n");
+    expected_contents += DescriptorToDot(class_name.c_str()) +
+        std::string("\n");
+  }
+  std::string output_file_contents;
+  ScratchFile profile_file;
+  EXPECT_TRUE(CreateProfile(input_file_contents,
+                            profile_file.GetFilename(),
+                            GetLibCoreDexFileNames()[0]));
+  ProfileCompilationInfo info;
+  profile_file.GetFile()->ResetOffset();
+  ASSERT_TRUE(info.Load(GetFd(profile_file)));
+  // Verify that the profile has matching methods.
+  ScopedObjectAccess soa(Thread::Current());
+  ObjPtr<mirror::Class> klass = GetClass(nullptr, "Ljava/lang/Math;");
+  ASSERT_TRUE(klass != nullptr);
+  size_t method_count = 0;
+  for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
+    if (!method.IsCopied() && method.GetCodeItem() != nullptr) {
+      ++method_count;
+      ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+      ASSERT_TRUE(info.GetMethod(method.GetDexFile()->GetLocation(),
+                                 method.GetDexFile()->GetLocationChecksum(),
+                                 method.GetDexMethodIndex(),
+                                 &pmi));
+    }
+  }
+  EXPECT_GT(method_count, 0u);
+}
+
 TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) {
   // Class names put here need to be in sorted order.
   std::vector<std::string> class_names = {
diff --git a/profman/profman.cc b/profman/profman.cc
index f7316cc..fdb9a75 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -120,7 +120,6 @@
   UsageError("");
   UsageError("  --create-profile-from=<filename>: creates a profile from a list of classes.");
   UsageError("");
-  UsageError("");
   UsageError("  --dex-location=<string>: location string to use with corresponding");
   UsageError("      apk-fd to find dex files");
   UsageError("");
@@ -140,6 +139,7 @@
 // Separators used when parsing human friendly representation of profiles.
 static const std::string kMethodSep = "->";
 static const std::string kMissingTypesMarker = "missing_types";
+static const std::string kClassAllMethods = "*";
 static constexpr char kProfileParsingInlineChacheSep = '+';
 static constexpr char kProfileParsingTypeSep = ',';
 static constexpr char kProfileParsingFirstCharInSignature = '(';
@@ -630,6 +630,7 @@
   // "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;".
   // "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types".
   // "LTestInline;->inlineNoInlineCaches(LSuper;)I".
+  // "LTestInline;->*".
   // The method and classes are searched only in the given dex files.
   bool ProcessLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
                    const std::string& line,
@@ -650,8 +651,8 @@
       return false;
     }
 
-    if (method_str.empty()) {
-      // No method to add. Just add the class.
+    if (method_str.empty() || method_str == kClassAllMethods) {
+      // Start by adding the class.
       std::set<DexCacheResolvedClasses> resolved_class_set;
       const DexFile* dex_file = class_ref.dex_file;
       const auto& dex_resolved_classes = resolved_class_set.emplace(
@@ -659,7 +660,27 @@
             dex_file->GetBaseLocation(),
             dex_file->GetLocationChecksum());
       dex_resolved_classes.first->AddClass(class_ref.type_index);
-      profile->AddMethodsAndClasses(std::vector<ProfileMethodInfo>(), resolved_class_set);
+      std::vector<ProfileMethodInfo> methods;
+      if (method_str == kClassAllMethods) {
+        // Add all of the methods.
+        const DexFile::ClassDef* class_def = dex_file->FindClassDef(class_ref.type_index);
+        const uint8_t* class_data = dex_file->GetClassData(*class_def);
+        if (class_data != nullptr) {
+          ClassDataItemIterator it(*dex_file, class_data);
+          while (it.HasNextStaticField() || it.HasNextInstanceField()) {
+            it.Next();
+          }
+          while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) {
+            if (it.GetMethodCodeItemOffset() != 0) {
+              // Add all of the methods that have code to the profile.
+              const uint32_t method_idx = it.GetMemberIndex();
+              methods.push_back(ProfileMethodInfo(dex_file, method_idx));
+            }
+            it.Next();
+          }
+        }
+      }
+      profile->AddMethodsAndClasses(methods, resolved_class_set);
       return true;
     }
 
diff --git a/runtime/arch/mips64/instruction_set_features_mips64_test.cc b/runtime/arch/mips64/instruction_set_features_mips64_test.cc
index 563200f..0ba0bd4 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64_test.cc
+++ b/runtime/arch/mips64/instruction_set_features_mips64_test.cc
@@ -20,7 +20,7 @@
 
 namespace art {
 
-TEST(Mips64InstructionSetFeaturesTest, Mips64Features) {
+TEST(Mips64InstructionSetFeaturesTest, Mips64FeaturesFromDefaultVariant) {
   std::string error_msg;
   std::unique_ptr<const InstructionSetFeatures> mips64_features(
       InstructionSetFeatures::FromVariant(kMips64, "default", &error_msg));
@@ -31,4 +31,20 @@
   EXPECT_EQ(mips64_features->AsBitmap(), 1U);
 }
 
+TEST(Mips64InstructionSetFeaturesTest, Mips64FeaturesFromR6Variant) {
+  std::string error_msg;
+  std::unique_ptr<const InstructionSetFeatures> mips64r6_features(
+      InstructionSetFeatures::FromVariant(kMips64, "mips64r6", &error_msg));
+  ASSERT_TRUE(mips64r6_features.get() != nullptr) << error_msg;
+  EXPECT_EQ(mips64r6_features->GetInstructionSet(), kMips64);
+  EXPECT_TRUE(mips64r6_features->Equals(mips64r6_features.get()));
+  EXPECT_STREQ("msa", mips64r6_features->GetFeatureString().c_str());
+  EXPECT_EQ(mips64r6_features->AsBitmap(), 1U);
+
+  std::unique_ptr<const InstructionSetFeatures> mips64_default_features(
+      InstructionSetFeatures::FromVariant(kMips64, "default", &error_msg));
+  ASSERT_TRUE(mips64_default_features.get() != nullptr) << error_msg;
+  EXPECT_TRUE(mips64r6_features->Equals(mips64_default_features.get()));
+}
+
 }  // namespace art
diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc
index d4bb56b..5394e53 100644
--- a/runtime/base/scoped_flock.cc
+++ b/runtime/base/scoped_flock.cc
@@ -116,7 +116,10 @@
 ScopedFlock::~ScopedFlock() {
   if (file_.get() != nullptr) {
     int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN));
-    CHECK_EQ(0, flock_result);
+    if (flock_result != 0) {
+      PLOG(FATAL) << "Unable to unlock file " << file_->GetPath();
+      UNREACHABLE();
+    }
     int close_result = -1;
     if (file_->ReadOnlyMode()) {
       close_result = file_->Close();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 746cace..fa87c8c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -6732,10 +6732,11 @@
     auto is_same_method = [m] (const ArtMethod& meth) {
       return &meth == m;
     };
-    CHECK((super_vtable_length > i && superclass->GetVTableEntry(i, pointer_size) == m) ||
-          std::find_if(virtuals.begin(), virtuals.end(), is_same_method) != virtuals.end())
-        << m->PrettyMethod() << " does not seem to be owned by current class "
-        << klass->PrettyClass() << " or any of its superclasses!";
+    if (!((super_vtable_length > i && superclass->GetVTableEntry(i, pointer_size) == m) ||
+          std::find_if(virtuals.begin(), virtuals.end(), is_same_method) != virtuals.end())) {
+      LOG(WARNING) << m->PrettyMethod() << " does not seem to be owned by current class "
+                   << klass->PrettyClass() << " or any of its superclasses!";
+    }
   }
 }
 
@@ -6763,14 +6764,15 @@
                                   other_entry->GetAccessFlags())) {
         continue;
       }
-      CHECK(vtable_entry != other_entry &&
-            !name_comparator.HasSameNameAndSignature(
-                other_entry->GetInterfaceMethodIfProxy(pointer_size)))
-          << "vtable entries " << i << " and " << j << " are identical for "
-          << klass->PrettyClass() << " in method " << vtable_entry->PrettyMethod() << " (0x"
-          << std::hex << reinterpret_cast<uintptr_t>(vtable_entry) << ") and "
-          << other_entry->PrettyMethod() << "  (0x" << std::hex
-          << reinterpret_cast<uintptr_t>(other_entry) << ")";
+      if (vtable_entry == other_entry ||
+          name_comparator.HasSameNameAndSignature(
+               other_entry->GetInterfaceMethodIfProxy(pointer_size))) {
+        LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
+                     << klass->PrettyClass() << " in method " << vtable_entry->PrettyMethod()
+                     << " (0x" << std::hex << reinterpret_cast<uintptr_t>(vtable_entry) << ") and "
+                     << other_entry->PrettyMethod() << "  (0x" << std::hex
+                     << reinterpret_cast<uintptr_t>(other_entry) << ")";
+      }
     }
   }
 }
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index b13d565..0aa04c1 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -227,7 +227,7 @@
     const int  sleep_max_ms = 2*1000;
     char       buff[5];
 
-    int sock = socket(PF_UNIX, SOCK_STREAM, 0);
+    int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
     if (sock < 0) {
       PLOG(ERROR) << "Could not create ADB control socket";
       return false;
@@ -264,7 +264,7 @@
        * up after a few minutes in case somebody ships an app with
        * the debuggable flag set.
        */
-      int  ret = connect(ControlSock(), &control_addr_.controlAddrPlain, control_addr_len_);
+      int ret = connect(ControlSock(), &control_addr_.controlAddrPlain, control_addr_len_);
       if (!ret) {
         int control_sock = ControlSock();
 #ifdef ART_TARGET_ANDROID
@@ -278,7 +278,7 @@
 
         /* now try to send our pid to the ADB daemon */
         ret = TEMP_FAILURE_RETRY(send(control_sock, buff, 4, 0));
-        if (ret >= 0) {
+        if (ret == 4) {
           VLOG(jdwp) << StringPrintf("PID sent as '%.*s' to ADB", 4, buff);
           break;
         }
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 30a4046..008c388 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -16,6 +16,10 @@
 
 #include "thread.h"
 
+#if !defined(__APPLE__)
+#include <sched.h>
+#endif
+
 #include <pthread.h>
 #include <signal.h>
 #include <sys/resource.h>
@@ -1591,8 +1595,21 @@
   if (thread != nullptr) {
     int policy;
     sched_param sp;
+#if !defined(__APPLE__)
+    // b/36445592 Don't use pthread_getschedparam since pthread may have exited.
+    policy = sched_getscheduler(tid);
+    if (policy == -1) {
+      PLOG(WARNING) << "sched_getscheduler(" << tid << ")";
+    }
+    int sched_getparam_result = sched_getparam(tid, &sp);
+    if (sched_getparam_result == -1) {
+      PLOG(WARNING) << "sched_getparam(" << tid << ", &sp)";
+      sp.sched_priority = -1;
+    }
+#else
     CHECK_PTHREAD_CALL(pthread_getschedparam, (thread->tlsPtr_.pthread_self, &policy, &sp),
                        __FUNCTION__);
+#endif
     os << " sched=" << policy << "/" << sp.sched_priority
        << " handle=" << reinterpret_cast<void*>(thread->tlsPtr_.pthread_self);
   }
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 54cce98..5aef062 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -34,7 +34,6 @@
 
 namespace art {
 
-jclass WellKnownClasses::com_android_dex_Dex;
 jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
 jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
 jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
@@ -267,7 +266,6 @@
 #undef STRING_INIT_LIST
 
 void WellKnownClasses::Init(JNIEnv* env) {
-  com_android_dex_Dex = CacheClass(env, "com/android/dex/Dex");
   dalvik_annotation_optimization_CriticalNative =
       CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
   dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index af4dbbf..c184731 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -44,7 +44,6 @@
 
   static ObjPtr<mirror::Class> ToClass(jclass global_jclass) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  static jclass com_android_dex_Dex;
   static jclass dalvik_annotation_optimization_CriticalNative;
   static jclass dalvik_annotation_optimization_FastNative;
   static jclass dalvik_system_BaseDexClassLoader;
diff --git a/test/080-oom-throw/run b/test/080-oom-throw/run
new file mode 100644
index 0000000..eb47378
--- /dev/null
+++ b/test/080-oom-throw/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+exec ${RUN} $@ --runtime-option -Xmx16m
diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java
index a6c18b7..3d5d062 100644
--- a/test/080-oom-throw/src/Main.java
+++ b/test/080-oom-throw/src/Main.java
@@ -114,13 +114,13 @@
     static Object[] holder;
 
     public static void blowup() throws Exception {
-        int size = 32 * 1024 * 1024;
+        int size = 2 * 1024 * 1024;
         for (int i = 0; i < holder.length; ) {
             try {
                 holder[i] = new char[size];
                 i++;
             } catch (OutOfMemoryError oome) {
-                size = size / 2;
+                size = size / 16;
                 if (size == 0) {
                      break;
                 }
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 0301621..2b57de6 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -229,12 +229,14 @@
 #     they are rewritten. These tests use a broken class loader that tries to
 #     register a dex file that's already registered with a different loader.
 #     b/34193123
+# Disable 638-checker-inline-caches until b/36371709 is fixed.
 ART_TEST_RUN_TEST_SKIP += \
   115-native-bridge \
   153-reference-stress \
   080-oom-fragmentation \
   497-inlining-and-class-loader \
-  542-unresolved-access-check
+  542-unresolved-access-check \
+  638-checker-inline-caches
 
 ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
         $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
@@ -444,12 +446,16 @@
 
 # This test fails without an image.
 # 018, 961, 964, 968 often time out. b/34369284
+# 508: b/36365552
+# 597: b/36467228
 TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
   137-cfi \
   138-duplicate-classes-check \
   018-stack-overflow \
   476-clinit-inline-static-invoke \
   496-checker-inlining-class-loader \
+  508-referrer-method \
+  597-deopt-new-string \
   637-checker-throw-inline \
   616-cha \
   616-cha-abstract \
diff --git a/test/DefaultMethods/IterableBase.java b/test/DefaultMethods/IterableBase.java
new file mode 100644
index 0000000..4cefdef
--- /dev/null
+++ b/test/DefaultMethods/IterableBase.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+interface Iface {
+    default void defaultMethod() {
+    }
+}
+
+class Impl implements Iface {
+}
+
+abstract class IterableBase implements Iterable {
+}
+
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f37029b..fd2a317 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -339,16 +339,33 @@
     {
         "tests": ["476-clinit-inline-static-invoke",
                   "496-checker-inlining-class-loader",
+                  "508-referrer-method",
                   "637-checker-throw-inline"],
         "bug": "http://b/36365552",
         "variant": "no-image & jit"
     },
     {
+        "tests": ["597-deopt-new-string"],
+        "bug": "http://b/36467228",
+        "variant": "no-image & jit"
+    },
+    {
         "tests": ["530-checker-lse",
                   "530-checker-lse2",
                   "030-bad-finalizer",
                   "080-oom-throw"],
         "bug": "http://b/36377828",
         "variant": "interp-ac"
+    },
+    {
+        "tests": "638-checker-inline-caches",
+        "description": ["Disable 638-checker-inline-caches temporarily until a fix",
+                        "arrives."],
+        "bug": "http://b/36371709"
+    },
+    {
+        "tests": "080-oom-throw",
+        "bug": "http://b/36501991",
+        "variant": "interpreter & target"
     }
 ]
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 4c519ae..835b678 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -46,7 +46,6 @@
 print custom_env
 os.environ.update(custom_env)
 
-
 if target.get('target'):
   build_command = 'make'
   build_command += ' -j' + str(n_threads)
@@ -56,7 +55,7 @@
   if subprocess.call(build_command.split()):
     sys.exit(1)
 
-else:
+if target.get('run-tests'):
   run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
                                    'art/test/testrunner/testrunner.py')]
   run_test_command += target.get('flags', [])
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 1af2ae7..5a6ecff 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -1,29 +1,35 @@
 target_config = {
     'art-test' : {
+        'target' : 'test-art-host-gtest',
+        'run-tests' : True,
         'flags' : [],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-interpreter' : {
+        'run-tests' : True,
         'flags' : ['--interpreter'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-interpreter-access-checks' : {
+        'run-tests' : True,
         'flags' : ['--interp-ac'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-jit' : {
+        'run-tests' : True,
         'flags' : ['--jit'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-gcstress-gcverify': {
+        'run-tests' : True,
         'flags' : ['--gcstress',
                    '--gcverify'],
         'env' : {
@@ -32,6 +38,7 @@
         }
     },
     'art-interpreter-gcstress' : {
+        'run-tests' : True,
         'flags': ['--interpreter',
                   '--gcstress'],
         'env' : {
@@ -40,6 +47,7 @@
         }
     },
     'art-optimizing-gcstress' : {
+        'run-tests' : True,
         'flags': ['--gcstress',
                   '--optimizing'],
         'env' : {
@@ -48,6 +56,7 @@
         }
     },
     'art-jit-gcstress' : {
+        'run-tests' : True,
         'flags': ['--jit',
                   '--gcstress'],
         'env' : {
@@ -56,6 +65,7 @@
         }
     },
     'art-read-barrier' : {
+        'run-tests' : True,
         'flags': ['--interpreter',
                   '--optimizing'],
         'env' : {
@@ -64,6 +74,7 @@
         }
     },
     'art-read-barrier-gcstress' : {
+        'run-tests' : True,
         'flags' : ['--interpreter',
                    '--optimizing',
                    '--gcstress'],
@@ -73,6 +84,7 @@
         }
     },
     'art-read-barrier-table-lookup' : {
+        'run-tests' : True,
         'flags' : ['--interpreter',
                    '--optimizing'],
         'env' : {
@@ -82,6 +94,7 @@
         }
     },
     'art-debug-gc' : {
+        'run-tests' : True,
         'flags' : ['--interpreter',
                    '--optimizing'],
         'env' : {
@@ -90,6 +103,7 @@
         }
     },
     'art-ss-gc' : {
+        'run-tests' : True,
         'flags' : ['--interpreter',
                    '--optimizing',
                    '--jit'],
@@ -99,6 +113,7 @@
         }
     },
     'art-gss-gc' : {
+        'run-tests' : True,
         'flags' : ['--interpreter',
                    '--optimizing',
                    '--jit'],
@@ -108,6 +123,7 @@
         }
     },
     'art-ss-gc-tlab' : {
+        'run-tests' : True,
         'flags' : ['--interpreter',
                    '--optimizing',
                    '--jit'],
@@ -118,6 +134,7 @@
         }
     },
     'art-gss-gc-tlab' : {
+        'run-tests' : True,
         'flags' : ['--interpreter',
                    '--optimizing',
                    '--jit'],
@@ -128,12 +145,14 @@
         }
     },
     'art-tracing' : {
+        'run-tests' : True,
         'flags' : ['--trace'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-interpreter-tracing' : {
+        'run-tests' : True,
         'flags' : ['--interpreter',
                    '--trace'],
         'env' : {
@@ -141,24 +160,28 @@
         }
     },
     'art-forcecopy' : {
+        'run-tests' : True,
         'flags' : ['--forcecopy'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-no-prebuild' : {
+        'run-tests' : True,
         'flags' : ['--no-prebuild'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-no-image' : {
+        'run-tests' : True,
         'flags' : ['--no-image'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-interpreter-no-image' : {
+        'run-tests' : True,
         'flags' : ['--interpreter',
                    '--no-image'],
         'env' : {
@@ -166,18 +189,21 @@
         }
     },
     'art-relocate-no-patchoat' : {
+        'run-tests' : True,
         'flags' : ['--relocate-npatchoat'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-no-dex2oat' : {
+        'run-tests' : True,
         'flags' : ['--no-dex2oat'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-heap-poisoning' : {
+        'run-tests' : True,
         'flags' : ['--interpreter',
                    '--optimizing'],
         'env' : {
diff --git a/tools/golem/build-target.sh b/tools/golem/build-target.sh
new file mode 100755
index 0000000..8d8e2bb
--- /dev/null
+++ b/tools/golem/build-target.sh
@@ -0,0 +1,384 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+if [[ ! -d art ]]; then
+  echo "Script needs to be run at the root of the android tree"
+  exit 1
+fi
+
+ALL_CONFIGS=(linux-ia32 linux-x64 linux-armv8 linux-armv7 android-armv8 android-armv7)
+
+usage() {
+  local config
+  local golem_target
+
+  (cat << EOF
+  Usage: $(basename "${BASH_SOURCE[0]}") [--golem=<target>] --machine-type=MACHINE_TYPE
+                 [--tarball[=<target>.tar.gz]]
+
+  Build minimal art binaries required to run golem benchmarks either
+  locally or on the golem servers.
+
+  Creates the \$MACHINE_TYPE binaries in your \$OUT_DIR, and if --tarball was specified,
+  it also tars the results of the build together into your <target.tar.gz> file.
+  --------------------------------------------------------
+  Required Flags:
+    --machine-type=MT   Specify the machine type that will be built.
+
+  Optional Flags":
+    --golem=<target>    Builds with identical commands that Golem servers use.
+    --tarball[=o.tgz]   Tar/gz the results. File name defaults to <machine_type>.tar.gz
+    -j<num>             Specify how many jobs to use for parallelism.
+    --help              Print this help listing.
+    --showcommands      Show commands as they are being executed.
+    --simulate          Print commands only, don't execute commands.
+EOF
+  ) | sed -e 's/^[[:space:]][[:space:]]//g' >&2 # Strip leading whitespace from heredoc.
+
+  echo >&2 "Available machine types:"
+  for config in "${ALL_CONFIGS[@]}"; do
+    echo >&2 "  $config"
+  done
+
+  echo >&2
+  echo >&2 "Available Golem targets:"
+  while IFS='' read -r golem_target; do
+    echo >&2 "  $golem_target"
+  done < <("$(thisdir)/env" --list-targets)
+}
+
+# Check if $1 element is in array $2
+contains_element() {
+  local e
+  for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
+  return 1
+}
+
+# Display a command, but don't execute it, if --showcommands was set.
+show_command() {
+  if [[ $showcommands == "showcommands" ]]; then
+    echo "$@"
+  fi
+}
+
+# Execute a command, displaying it if --showcommands was set.
+# If --simulate is used, command is not executed.
+execute() {
+  show_command "$@"
+  execute_noshow "$@"
+}
+
+# Execute a command unless --simulate was used.
+execute_noshow() {
+  if [[ $simulate == "simulate" ]]; then
+    return 0
+  fi
+
+  local prog="$1"
+  shift
+  "$prog" "$@"
+}
+
+# Export environment variable, echoing it to screen.
+setenv() {
+  local name="$1"
+  local value="$2"
+
+  export $name="$value"
+  echo export $name="$value"
+}
+
+# Export environment variable, echoing $3 to screen ($3 is meant to be unevaluated).
+setenv_escape() {
+  local name="$1"
+  local value="$2"
+  local escaped_value="$3"
+
+  export $name="$value"
+  echo export $name="$escaped_value"
+}
+
+log_usage_error() {
+  echo >&2 "ERROR: " "$@"
+  echo >&2 "       See --help for the correct usage information."
+  exit 1
+}
+
+log_fatal() {
+  echo >&2 "FATAL: " "$@"
+  exit 2
+}
+
+# Get the directory of this script.
+thisdir() {
+  (\cd "$(dirname "${BASH_SOURCE[0]}")" && pwd )
+}
+
+# Get the path to the top of the Android source tree.
+gettop() {
+  if [[ "x$ANDROID_BUILD_TOP" != "x" ]]; then
+    echo "$ANDROID_BUILD_TOP";
+  else
+    echo "$(thisdir)/../../.."
+  fi
+}
+
+# Get a build variable from the Android build system.
+get_build_var() {
+  local varname="$1"
+
+  # include the desired target product/build-variant
+  # which won't be set in our env if neither we nor the user first executed
+  # source build/envsetup.sh (e.g. if simulating from a fresh shell).
+  local extras
+  [[ -n $target_product ]] && extras+=" TARGET_PRODUCT=$target_product"
+  [[ -n $target_build_variant ]] && extras+=" TARGET_BUILD_VARIANT=$target_build_variant"
+
+  # call dumpvar-$name from the makefile system.
+  (\cd "$(gettop)";
+  CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
+    command make --no-print-directory -f build/core/config.mk \
+    $extras \
+    dumpvar-$varname)
+}
+
+# Defaults from command-line.
+
+mode=""  # blank or 'golem' if --golem was specified.
+golem_target="" # --golem=$golem_target
+config="" # --machine-type=$config
+j_arg="-j8"
+showcommands=""
+simulate=""
+make_tarball=""
+tarball=""
+
+# Parse command line arguments
+
+while [[ "$1" != "" ]]; do
+  case "$1" in
+    --help)
+      usage
+      exit 1
+      ;;
+    --golem=*)
+      mode="golem"
+      golem_target="${1##--golem=}"
+
+      if [[ "x$golem_target" == x ]]; then
+        log_usage_error "Missing --golem target type."
+      fi
+
+      shift
+      ;;
+    --machine-type=*)
+      config="${1##--machine-type=}"
+      if ! contains_element "$config" "${ALL_CONFIGS[@]}"; then
+        log_usage_error "Invalid --machine-type value '$config'"
+      fi
+      shift
+      ;;
+    --tarball)
+      tarball="" # reuse the machine type name.
+      make_tarball="make_tarball"
+      shift
+      ;;
+    --tarball=*)
+      tarball="${1##--tarball=}"
+      make_tarball="make_tarball"
+      shift
+      ;;
+    -j*)
+      j_arg="$1"
+      shift
+      ;;
+    --showcommands)
+      showcommands="showcommands"
+      shift
+      ;;
+    --simulate)
+      simulate="simulate"
+      shift
+      ;;
+    *)
+      log_usage_error "Unknown options $1"
+      ;;
+  esac
+done
+
+###################################
+###################################
+###################################
+
+if [[ -z $config ]]; then
+  log_usage_error "--machine-type option is required."
+fi
+
+# --tarball defaults to the --machine-type value with .tar.gz.
+tarball="${tarball:-$config.tar.gz}"
+
+target_product="$TARGET_PRODUCT"
+target_build_variant="$TARGET_BUILD_VARIANT"
+
+# If not using --golem, use whatever the user had lunch'd prior to this script.
+if [[ $mode == "golem" ]]; then
+  # This section is intended solely to be executed by a golem build server.
+
+  target_build_variant=eng
+  case "$config" in
+    *-armv7)
+      target_product="arm_krait"
+      ;;
+    *-armv8)
+      target_product="armv8"
+      ;;
+    *)
+      target_product="sdk"
+      ;;
+  esac
+
+  if [[ $target_product = arm* ]]; then
+    # If using the regular manifest, e.g. 'master'
+    # The lunch command for arm will assuredly fail because we don't have device/generic/art.
+    #
+    # Print a human-readable error message instead of trying to lunch and failing there.
+    if ! [[ -d "$(gettop)/device/generic/art" ]]; then
+      log_fatal "Missing device/generic/art directory. Perhaps try master-art repo manifest?\n" \
+                "       Cannot build ARM targets (arm_krait, armv8) for Golem." >&2
+    fi
+    # We could try to keep on simulating but it seems brittle because we won't have the proper
+    # build variables to output the right strings.
+  fi
+
+  # Get this particular target's environment variables (e.g. ART read barrier on/off).
+  source "$(thisdir)"/env "$golem_target" || exit 1
+
+  lunch_target="$target_product-$target_build_variant"
+
+  execute 'source' build/envsetup.sh
+  # Build generic targets (as opposed to something specific like aosp_angler-eng).
+  execute lunch "$lunch_target"
+  setenv JACK_SERVER false
+  setenv_escape JACK_REPOSITORY "$PWD/prebuilts/sdk/tools/jacks" '$PWD/prebuilts/sdk/tools/jacks'
+  # Golem uses master-art repository which is missing a lot of other libraries.
+  setenv SOONG_ALLOW_MISSING_DEPENDENCIES true
+  # Golem may be missing tools such as javac from its path.
+  setenv_escape PATH "/usr/lib/jvm/java-8-openjdk-amd64/bin/:$PATH" '/usr/lib/jvm/java-8-openjdk-amd64/bin/:$PATH'
+else
+  # Look up the default variables from the build system if they weren't set already.
+  [[ -z $target_product ]] && target_product="$(get_build_var TARGET_PRODUCT)"
+  [[ -z $target_build_variant ]] && target_build_variant="$(get_build_var TARGET_BUILD_VARIANT)"
+fi
+
+# Defaults for all machine types.
+make_target="build-art-target-golem"
+out_dir="out/x86_64"
+root_dir_var="PRODUCT_OUT"
+strip_symbols=false
+bit64_suffix=""
+tar_directories=(system data/art-test)
+
+# Per-machine type overrides
+if [[ $config == linux-arm* ]]; then
+    setenv ART_TARGET_LINUX true
+fi
+
+case "$config" in
+  linux-ia32|linux-x64)
+    root_dir_var="HOST_OUT"
+    # Android strips target builds automatically, but not host builds.
+    strip_symbols=true
+    make_target="build-art-host-golem"
+
+    if [[ $config == linux-ia32 ]]; then
+      out_dir="out/x86"
+      setenv HOST_PREFER_32_BIT true
+    else
+      bit64_suffix="64"
+    fi
+
+    tar_directories=(bin framework usr lib${bit64_suffix})
+    ;;
+  *-armv8)
+    bit64_suffix="64"
+    ;;
+  *-armv7)
+    ;;
+  *)
+    log_fatal "Unsupported machine-type '$config'"
+esac
+
+# Golem benchmark run commands expect a certain $OUT_DIR to be set,
+# so specify it here.
+#
+# Note: It is questionable if we want to customize this since users
+# could alternatively probably use their own build directly (and forgo this script).
+setenv OUT_DIR "$out_dir"
+root_dir="$(get_build_var "$root_dir_var")"
+
+if [[ $mode == "golem" ]]; then
+  # For golem-style running only.
+  # Sets the DT_INTERP to this path in every .so we can run the
+  # non-system version of dalvikvm with our own copies of the dependencies (e.g. our own libc++).
+  if [[ $config == android-* ]]; then
+    # TODO: the linker can be relative to the binaries
+    # (which is what we do for linux-armv8 and linux-armv7)
+    golem_run_path="/data/local/tmp/runner/"
+  else
+    golem_run_path=""
+  fi
+
+  # Only do this for target builds. Host doesn't need this.
+  if [[ $config == *-arm* ]]; then
+    setenv CUSTOM_TARGET_LINKER "${golem_run_path}${root_dir}/system/bin/linker${bit64_suffix}"
+  fi
+fi
+
+#
+# Main command execution below here.
+# (everything prior to this just sets up environment variables,
+#  and maybe calls lunch).
+#
+
+execute make "${j_arg}" "${make_target}"
+
+if $strip_symbols; then
+  # Further reduce size by stripping symbols.
+  execute_noshow strip $root_dir/bin/* || true
+  show_command strip $root_dir/bin/'*'  '|| true'
+  execute_noshow strip $root_dir/lib${bit64_suffix}/'*'
+  show_command strip $root_dir/lib${bit64_suffix}/'*'
+fi
+
+if [[ "$make_tarball" == "make_tarball" ]]; then
+  # Create a tarball which is required for the golem build resource.
+  # (In particular, each golem benchmark's run commands depend on a list of resource files
+  #  in order to have all the files it needs to actually execute,
+  #  and this tarball would satisfy that particular target+machine-type's requirements).
+  dirs_rooted=()
+  for tar_dir in "${tar_directories[@]}"; do
+    dirs_rooted+=("$root_dir/$tar_dir")
+  done
+
+  execute tar -czf "${tarball}" "${dirs_rooted[@]}" --exclude .git --exclude .gitignore
+  tar_result=$?
+  if [[ $tar_result -ne 0 ]]; then
+    [[ -f $tarball ]] && rm $tarball
+  fi
+
+  show_command '[[ $? -ne 0 ]] && rm' "$tarball"
+fi
+
diff --git a/tools/golem/env b/tools/golem/env
new file mode 100755
index 0000000..187ba3a
--- /dev/null
+++ b/tools/golem/env
@@ -0,0 +1,117 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+#
+# Export some environment variables used by ART's Android.mk/Android.bp
+# build systems to configure ART [to use a different implementation].
+#
+# Currently only varies on ART_USE_READ_BARRIER for a concurrent/non-concurrent
+# flavor of the ART garbage collector.
+#
+# Only meant for golem use since when building ART directly, one can/should set
+# these environment flags themselves.
+#
+# These environment flags are not really meant here to be for "correctness",
+# but rather telling the ART C++ to use alternative algorithms.
+# In other words, the same exact binary build with a different "target"
+# should run in the same context (e.g. it does not change arch or the OS it's built for).
+#
+
+setenv() {
+  local name="$1"
+  local value="$2"
+
+  export $name="$value"
+  echo export $name="$value"
+}
+
+# Enforce specified target-name is one of these.
+# Perhaps we should be less strict?
+ALL_TARGETS=(art-interpreter art-opt art-jit art-jit-cc art-opt-cc art-opt-debuggable art-vdex)
+
+usage() {
+  echo >&2 "Usage: $(basename $0) (--list-targets | <target-name>)"
+  echo >&2
+  echo >&2 "Exports the necessary ART environment variables"
+  echo >&2 "to pass to the Golem build to correctly configure ART."
+  echo >&2 "--------------------------------------------------------"
+  echo >&2 "Required Arguments:"
+  echo >&2 "  <target-name>       Specify the golem target to get environment variables for."
+  echo >&2
+  echo >&2 "Optional Flags":
+  echo >&2 "  --list-targets      Display all the targets. Do not require the main target-name."
+  echo >&2 "  --help              Print this help listing."
+  echo >&2
+  echo >&2 "Available Targets:"
+
+  list_targets 2 "  "
+}
+
+list_targets() {
+  local out_fd="${1:-1}" # defaults to 1 if no param was set
+  local prefix="$2"
+
+  for target in "${ALL_TARGETS[@]}"; do
+    echo >&$out_fd "${prefix}${target}"
+  done
+}
+
+
+# Check if $1 element is in array $2
+contains_element() {
+  local e
+  for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
+  return 1
+}
+
+main() {
+  if [[ $# -lt 1 ]]; then
+    usage
+    exit 1
+  fi
+
+  if [[ "$1" == "--help" ]]; then
+    usage
+    exit 1
+  fi
+
+  if [[ "$1" == "--list-targets" ]]; then
+    list_targets
+    exit 0
+  fi
+
+  local selected_target="$1"
+  if ! contains_element "$selected_target" "${ALL_TARGETS[@]}"; then
+    echo "ERROR: Invalid target value '$selected_target'" >&2
+    exit 1
+  fi
+
+  case "$selected_target" in
+    *-cc)
+      setenv ART_USE_READ_BARRIER true
+      ;;
+    *)
+      setenv ART_USE_READ_BARRIER false
+      ;;
+  esac
+
+  # Make smaller .tar.gz files by excluding debug targets.
+  setenv ART_BUILD_TARGET_DEBUG false
+  setenv ART_BUILD_HOST_DEBUG false
+  setenv USE_DEX2OAT_DEBUG false
+}
+
+main "$@"