Merge "Fix some memory leaks found by valgrind."
diff --git a/build/art.go b/build/art.go
index f305c07..d41b407 100644
--- a/build/art.go
+++ b/build/art.go
@@ -137,12 +137,42 @@
 	p.Target.Android.Cflags = deviceFlags(ctx)
 	p.Target.Host.Cflags = hostFlags(ctx)
 	ctx.AppendProperties(p)
+
+	if envTrue(ctx, "HOST_PREFER_32_BIT") {
+		type props struct {
+			Target struct {
+				Host struct {
+					Compile_multilib string
+				}
+			}
+		}
+
+		p := &props{}
+		p.Target.Host.Compile_multilib = "prefer32"
+		ctx.AppendProperties(p)
+	}
 }
 
 type artGlobalDefaults struct{}
 
+func (a *artLinkerCustomizer) CustomizeProperties(ctx android.CustomizePropertiesContext) {
+	linker := envDefault(ctx, "CUSTOM_TARGET_LINKER", "")
+	if linker != "" {
+		type props struct {
+			DynamicLinker string
+		}
+
+		p := &props{}
+		p.DynamicLinker = linker
+		ctx.AppendProperties(p)
+	}
+}
+
+type artLinkerCustomizer struct{}
+
 func init() {
 	soong.RegisterModuleType("art_cc_library", artLibrary)
+	soong.RegisterModuleType("art_cc_binary", artBinary)
 	soong.RegisterModuleType("art_cc_defaults", artDefaultsFactory)
 	soong.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
 }
@@ -173,6 +203,14 @@
 	return module, props
 }
 
+func artBinary() (blueprint.Module, []interface{}) {
+	binary, _ := cc.NewBinary(android.HostAndDeviceSupported)
+	module, props := binary.Init()
+
+	android.AddCustomizer(binary, &artLinkerCustomizer{})
+	return module, props
+}
+
 func envDefault(ctx android.BaseContext, key string, defaultValue string) string {
 	ret := ctx.AConfig().Getenv(key)
 	if ret == "" {
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2ef1802..5301a6b 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -247,35 +247,6 @@
   DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM);
 };
 
-class LoadStringSlowPathARM : public SlowPathCode {
- public:
-  explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCode(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
-
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
-
-    InvokeRuntimeCallingConvention calling_convention;
-    const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
-    __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index);
-    arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
-    arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
-
-    RestoreLiveRegisters(codegen, locations);
-    __ b(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM);
-};
-
 class TypeCheckSlowPathARM : public SlowPathCode {
  public:
   TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal)
@@ -5337,17 +5308,6 @@
 
 HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind(
     HLoadClass::LoadKind desired_class_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_class_load_kind) {
-      case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadClass::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadClass::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_class_load_kind) {
     case HLoadClass::LoadKind::kReferrersClass:
       break;
@@ -5389,11 +5349,12 @@
     return;
   }
 
-  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
       ? LocationSummary::kCallOnSlowPath
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
-  if (kUseBakerReadBarrier && !cls->NeedsEnvironment()) {
+  if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
     locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
   }
 
@@ -5418,6 +5379,7 @@
   Location out_loc = locations->Out();
   Register out = out_loc.AsRegister<Register>();
 
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   bool generate_null_check = false;
   switch (cls->GetLoadKind()) {
     case HLoadClass::LoadKind::kReferrersClass: {
@@ -5425,18 +5387,21 @@
       DCHECK(!cls->MustGenerateClinitCheck());
       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
       Register current_method = locations->InAt(0).AsRegister<Register>();
-      GenerateGcRootFieldLoad(
-          cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              current_method,
+                              ArtMethod::DeclaringClassOffset().Int32Value(),
+                              requires_read_barrier);
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       __ LoadLiteral(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
                                                                     cls->GetTypeIndex()));
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       CodeGeneratorARM::PcRelativePatchInfo* labels =
           codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
       __ BindTrackedLabel(&labels->movw_label);
@@ -5448,7 +5413,7 @@
       break;
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       DCHECK_NE(cls->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
       __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
@@ -5468,7 +5433,7 @@
       uint32_t offset = address & MaxInt<uint32_t>(offset_bits);
       __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address));
       // /* GcRoot<mirror::Class> */ out = *(base_address + offset)
-      GenerateGcRootFieldLoad(cls, out_loc, out, offset);
+      GenerateGcRootFieldLoad(cls, out_loc, out, offset, requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -5477,7 +5442,7 @@
       HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase();
       int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset();
       // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset)
-      GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset);
+      GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset, requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -5491,7 +5456,7 @@
                         ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value());
       // /* GcRoot<mirror::Class> */ out = out[type_index]
       size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
-      GenerateGcRootFieldLoad(cls, out_loc, out, offset);
+      GenerateGcRootFieldLoad(cls, out_loc, out, offset, requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
     }
   }
@@ -5583,28 +5548,28 @@
 }
 
 void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
-      ? LocationSummary::kCallOnSlowPath
+  LocationSummary::CallKind call_kind = load->NeedsEnvironment()
+      ? LocationSummary::kCallOnMainOnly
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
-  if (kUseBakerReadBarrier && !load->NeedsEnvironment()) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
-  }
 
   HLoadString::LoadKind load_kind = load->GetLoadKind();
-  if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod ||
-      load_kind == HLoadString::LoadKind::kDexCachePcRelative) {
+  DCHECK(load_kind != HLoadString::LoadKind::kDexCachePcRelative) << "Not supported";
+  if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
     locations->SetInAt(0, Location::RequiresRegister());
+    locations->SetOut(Location::RegisterLocation(R0));
+  } else {
+    locations->SetOut(Location::RequiresRegister());
   }
-  locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) {
   LocationSummary* locations = load->GetLocations();
   Location out_loc = locations->Out();
   Register out = out_loc.AsRegister<Register>();
+  HLoadString::LoadKind load_kind = load->GetLoadKind();
 
-  switch (load->GetLoadKind()) {
+  switch (load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
       DCHECK(!kEmitCompilerReadBarrier);
       __ LoadLiteral(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
@@ -5634,11 +5599,12 @@
       break;
   }
 
-  // TODO: Re-add the compiler code to do string dex cache lookup again.
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
-  codegen_->AddSlowPath(slow_path);
-  __ b(slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+  // TODO: Consider re-adding the compiler code to do string dex cache lookup again.
+  DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod);
+  InvokeRuntimeCallingConvention calling_convention;
+  __ LoadImmediate(calling_convention.GetRegisterAt(0), load->GetStringIndex());
+  codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
+  CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
 }
 
 static int32_t GetExceptionTlsOffset() {
@@ -6403,9 +6369,11 @@
 void InstructionCodeGeneratorARM::GenerateGcRootFieldLoad(HInstruction* instruction,
                                                           Location root,
                                                           Register obj,
-                                                          uint32_t offset) {
+                                                          uint32_t offset,
+                                                          bool requires_read_barrier) {
   Register root_reg = root.AsRegister<Register>();
-  if (kEmitCompilerReadBarrier) {
+  if (requires_read_barrier) {
+    DCHECK(kEmitCompilerReadBarrier);
     if (kUseBakerReadBarrier) {
       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
       // Baker's read barrier are used:
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index ac10e23..ce9d7e6 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -271,11 +271,12 @@
   //
   //   root <- *(obj + offset)
   //
-  // while honoring read barriers (if any).
+  // while honoring read barriers if requires_read_barrier is true.
   void GenerateGcRootFieldLoad(HInstruction* instruction,
                                Location root,
                                Register obj,
-                               uint32_t offset);
+                               uint32_t offset,
+                               bool requires_read_barrier = kEmitCompilerReadBarrier);
   void GenerateTestAndBranch(HInstruction* instruction,
                              size_t condition_input_index,
                              Label* true_target,
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index ceceedd..36f7b4d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4044,17 +4044,6 @@
 
 HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind(
     HLoadClass::LoadKind desired_class_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_class_load_kind) {
-      case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadClass::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadClass::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_class_load_kind) {
     case HLoadClass::LoadKind::kReferrersClass:
       break;
@@ -4089,11 +4078,12 @@
     return;
   }
 
-  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
       ? LocationSummary::kCallOnSlowPath
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
-  if (kUseBakerReadBarrier && !cls->NeedsEnvironment()) {
+  if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
     locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
   }
 
@@ -4116,6 +4106,7 @@
   Location out_loc = cls->GetLocations()->Out();
   Register out = OutputRegister(cls);
 
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   bool generate_null_check = false;
   switch (cls->GetLoadKind()) {
     case HLoadClass::LoadKind::kReferrersClass: {
@@ -4123,17 +4114,21 @@
       DCHECK(!cls->MustGenerateClinitCheck());
       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
       Register current_method = InputRegisterAt(cls, 0);
-      GenerateGcRootFieldLoad(
-          cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              current_method,
+                              ArtMethod::DeclaringClassOffset().Int32Value(),
+                              /*fixup_label*/ nullptr,
+                              requires_read_barrier);
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       __ Ldr(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
                                                             cls->GetTypeIndex()));
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       // Add ADRP with its PC-relative type patch.
       const DexFile& dex_file = cls->GetDexFile();
       uint32_t type_index = cls->GetTypeIndex();
@@ -4154,7 +4149,7 @@
       break;
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       DCHECK(cls->GetAddress() != 0u && IsUint<32>(cls->GetAddress()));
       __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(cls->GetAddress()));
       break;
@@ -4172,7 +4167,12 @@
       uint32_t offset = cls->GetAddress() & MaxInt<uint64_t>(offset_bits);
       __ Ldr(out.X(), codegen_->DeduplicateDexCacheAddressLiteral(base_address));
       // /* GcRoot<mirror::Class> */ out = *(base_address + offset)
-      GenerateGcRootFieldLoad(cls, out_loc, out.X(), offset);
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              out.X(),
+                              offset,
+                              /*fixup_label*/ nullptr,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -4191,7 +4191,12 @@
       vixl::aarch64::Label* ldr_label =
           codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label);
       // /* GcRoot<mirror::Class> */ out = *(base_address + offset)  /* PC-relative */
-      GenerateGcRootFieldLoad(cls, out_loc, out.X(), /* offset placeholder */ 0, ldr_label);
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              out.X(),
+                              /* offset placeholder */ 0,
+                              ldr_label,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -4203,8 +4208,12 @@
       Register current_method = InputRegisterAt(cls, 0);
       __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value()));
       // /* GcRoot<mirror::Class> */ out = out[type_index]
-      GenerateGcRootFieldLoad(
-          cls, out_loc, out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              out.X(),
+                              CodeGenerator::GetCacheOffset(cls->GetTypeIndex()),
+                              /*fixup_label*/ nullptr,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -5106,9 +5115,11 @@
                                                             Location root,
                                                             Register obj,
                                                             uint32_t offset,
-                                                            vixl::aarch64::Label* fixup_label) {
+                                                            vixl::aarch64::Label* fixup_label,
+                                                            bool requires_read_barrier) {
   Register root_reg = RegisterFrom(root, Primitive::kPrimNot);
-  if (kEmitCompilerReadBarrier) {
+  if (requires_read_barrier) {
+    DCHECK(kEmitCompilerReadBarrier);
     if (kUseBakerReadBarrier) {
       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
       // Baker's read barrier are used:
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 78db803..f0d7910 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -294,7 +294,8 @@
                                Location root,
                                vixl::aarch64::Register obj,
                                uint32_t offset,
-                               vixl::aarch64::Label* fixup_label = nullptr);
+                               vixl::aarch64::Label* fixup_label = nullptr,
+                               bool requires_read_barrier = kEmitCompilerReadBarrier);
 
   // Generate a floating-point comparison.
   void GenerateFcmp(HInstruction* instruction);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 8858def..4689ccb 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -5961,17 +5961,6 @@
 
 HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind(
     HLoadClass::LoadKind desired_class_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_class_load_kind) {
-      case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadClass::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadClass::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_class_load_kind) {
     case HLoadClass::LoadKind::kReferrersClass:
       break;
@@ -6013,11 +6002,12 @@
     return;
   }
 
-  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
       ? LocationSummary::kCallOnSlowPath
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
-  if (kUseBakerReadBarrier && !cls->NeedsEnvironment()) {
+  if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
     locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
   }
 
@@ -6044,6 +6034,7 @@
   Register out = out_loc.AsRegister<Register>();
 
   bool generate_null_check = false;
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   switch (cls->GetLoadKind()) {
     case HLoadClass::LoadKind::kReferrersClass: {
       DCHECK(!cls->CanCallRuntime());
@@ -6051,24 +6042,28 @@
       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
       Register current_method = locations->InAt(0).AsRegister<Register>();
       GenerateGcRootFieldLoad(
-          cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
+          cls,
+          out_loc,
+          Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()),
+          /*fixup_label*/ nullptr,
+          requires_read_barrier);
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       __ movl(out, Immediate(/* placeholder */ 0));
       codegen_->RecordTypePatch(cls);
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       Register method_address = locations->InAt(0).AsRegister<Register>();
       __ leal(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset));
       codegen_->RecordTypePatch(cls);
       break;
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       DCHECK_NE(cls->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
       __ movl(out, Immediate(address));
@@ -6079,7 +6074,11 @@
       DCHECK_NE(cls->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
       // /* GcRoot<mirror::Class> */ out = *address
-      GenerateGcRootFieldLoad(cls, out_loc, Address::Absolute(address));
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              Address::Absolute(address),
+                              /*fixup_label*/ nullptr,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -6088,8 +6087,11 @@
       uint32_t offset = cls->GetDexCacheElementOffset();
       Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), offset);
       // /* GcRoot<mirror::Class> */ out = *(base + offset)  /* PC-relative */
-      GenerateGcRootFieldLoad(
-          cls, out_loc, Address(base_reg, CodeGeneratorX86::kDummy32BitOffset), fixup_label);
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              Address(base_reg, CodeGeneratorX86::kDummy32BitOffset),
+                              fixup_label,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -6100,8 +6102,11 @@
       __ movl(out, Address(current_method,
                            ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value()));
       // /* GcRoot<mirror::Class> */ out = out[type_index]
-      GenerateGcRootFieldLoad(
-          cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())),
+                              /*fixup_label*/ nullptr,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -6938,9 +6943,11 @@
 void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad(HInstruction* instruction,
                                                           Location root,
                                                           const Address& address,
-                                                          Label* fixup_label) {
+                                                          Label* fixup_label,
+                                                          bool requires_read_barrier) {
   Register root_reg = root.AsRegister<Register>();
-  if (kEmitCompilerReadBarrier) {
+  if (requires_read_barrier) {
+    DCHECK(kEmitCompilerReadBarrier);
     if (kUseBakerReadBarrier) {
       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
       // Baker's read barrier are used:
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index df65fa2..e225098 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -263,7 +263,8 @@
   void GenerateGcRootFieldLoad(HInstruction* instruction,
                                Location root,
                                const Address& address,
-                               Label* fixup_label = nullptr);
+                               Label* fixup_label = nullptr,
+                               bool requires_read_barrier = kEmitCompilerReadBarrier);
 
   // Push value to FPU stack. `is_fp` specifies whether the value is floating point or not.
   // `is_wide` specifies whether it is long/double or not.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 5230269..a21a09e 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5408,17 +5408,6 @@
 
 HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind(
     HLoadClass::LoadKind desired_class_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_class_load_kind) {
-      case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadClass::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadClass::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_class_load_kind) {
     case HLoadClass::LoadKind::kReferrersClass:
       break;
@@ -5454,11 +5443,12 @@
     return;
   }
 
-  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
       ? LocationSummary::kCallOnSlowPath
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
-  if (kUseBakerReadBarrier && !cls->NeedsEnvironment()) {
+  if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
     locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
   }
 
@@ -5482,6 +5472,7 @@
   Location out_loc = locations->Out();
   CpuRegister out = out_loc.AsRegister<CpuRegister>();
 
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   bool generate_null_check = false;
   switch (cls->GetLoadKind()) {
     case HLoadClass::LoadKind::kReferrersClass: {
@@ -5490,16 +5481,20 @@
       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
       CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
       GenerateGcRootFieldLoad(
-          cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
+          cls,
+          out_loc,
+          Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()),
+          /*fixup_label*/nullptr,
+          requires_read_barrier);
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       __ leal(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false));
       codegen_->RecordTypePatch(cls);
       break;
     case HLoadClass::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       DCHECK_NE(cls->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
       __ movl(out, Immediate(address));  // Zero-extended.
@@ -5511,11 +5506,19 @@
       // /* GcRoot<mirror::Class> */ out = *address
       if (IsUint<32>(cls->GetAddress())) {
         Address address = Address::Absolute(cls->GetAddress(), /* no_rip */ true);
-        GenerateGcRootFieldLoad(cls, out_loc, address);
+        GenerateGcRootFieldLoad(cls,
+                                out_loc,
+                                address,
+                                /*fixup_label*/nullptr,
+                                requires_read_barrier);
       } else {
         // TODO: Consider using opcode A1, i.e. movl eax, moff32 (with 64-bit address).
         __ movq(out, Immediate(cls->GetAddress()));
-        GenerateGcRootFieldLoad(cls, out_loc, Address(out, 0));
+        GenerateGcRootFieldLoad(cls,
+                                out_loc,
+                                Address(out, 0),
+                                /*fixup_label*/nullptr,
+                                requires_read_barrier);
       }
       generate_null_check = !cls->IsInDexCache();
       break;
@@ -5526,7 +5529,7 @@
       Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset,
                                           /* no_rip */ false);
       // /* GcRoot<mirror::Class> */ out = *address  /* PC-relative */
-      GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label);
+      GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -5539,7 +5542,11 @@
                       ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value()));
       // /* GcRoot<mirror::Class> */ out = out[type_index]
       GenerateGcRootFieldLoad(
-          cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+          cls,
+          out_loc,
+          Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())),
+          /*fixup_label*/nullptr,
+          requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -6387,9 +6394,11 @@
 void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(HInstruction* instruction,
                                                              Location root,
                                                              const Address& address,
-                                                             Label* fixup_label) {
+                                                             Label* fixup_label,
+                                                             bool requires_read_barrier) {
   CpuRegister root_reg = root.AsRegister<CpuRegister>();
-  if (kEmitCompilerReadBarrier) {
+  if (requires_read_barrier) {
+    DCHECK(kEmitCompilerReadBarrier);
     if (kUseBakerReadBarrier) {
       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
       // Baker's read barrier are used:
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index f23bff5..d939083 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -257,7 +257,8 @@
   void GenerateGcRootFieldLoad(HInstruction* instruction,
                                Location root,
                                const Address& address,
-                               Label* fixup_label = nullptr);
+                               Label* fixup_label = nullptr,
+                               bool requires_read_barrier = kEmitCompilerReadBarrier);
 
   void PushOntoFPStack(Location source, uint32_t temp_offset,
                        uint32_t stack_adjustment, bool is_float);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 1e5f0b6..ce53134 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -452,7 +452,8 @@
                                                                is_referrer,
                                                                invoke_instruction->GetDexPc(),
                                                                /* needs_access_check */ false,
-                                                               /* is_in_dex_cache */ true);
+                                                               /* is_in_dex_cache */ true,
+                                                               /* is_in_boot_image */ false);
 
   HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
   // TODO: Extend reference type propagation to understand the guard.
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 32dcc28..d7e4c53 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -935,7 +935,8 @@
       IsOutermostCompilingClass(type_index),
       dex_pc,
       needs_access_check,
-      /* is_in_dex_cache */ false);
+      /* is_in_dex_cache */ false,
+      /* is_in_boot_image */ false);
 
   AppendInstruction(load_class);
   HInstruction* cls = load_class;
@@ -1026,7 +1027,8 @@
         is_outer_class,
         dex_pc,
         /*needs_access_check*/ false,
-        /* is_in_dex_cache */ false);
+        /* is_in_dex_cache */ false,
+        /* is_in_boot_image */ false);
     AppendInstruction(load_class);
     clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
     AppendInstruction(clinit_check);
@@ -1384,7 +1386,8 @@
                                                  is_outer_class,
                                                  dex_pc,
                                                  /*needs_access_check*/ false,
-                                                 /* is_in_dex_cache */ false);
+                                                 /* is_in_dex_cache */ false,
+                                                 /* is_in_boot_image */ false);
   AppendInstruction(constant);
 
   HInstruction* cls = constant;
@@ -1659,7 +1662,8 @@
       IsOutermostCompilingClass(type_index),
       dex_pc,
       !can_access,
-      /* is_in_dex_cache */ false);
+      /* is_in_dex_cache */ false,
+      /* is_in_boot_image */ false);
   AppendInstruction(cls);
 
   TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
@@ -2634,7 +2638,8 @@
           IsOutermostCompilingClass(type_index),
           dex_pc,
           !can_access,
-          /* is_in_dex_cache */ false));
+          /* is_in_dex_cache */ false,
+          /* is_in_boot_image */ false));
       UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
       break;
     }
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 19e499b..149a71d 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -5461,7 +5461,8 @@
              bool is_referrers_class,
              uint32_t dex_pc,
              bool needs_access_check,
-             bool is_in_dex_cache)
+             bool is_in_dex_cache,
+             bool is_in_boot_image)
       : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
         special_input_(HUserRecord<HInstruction*>(current_method)),
         type_index_(type_index),
@@ -5475,6 +5476,7 @@
         is_referrers_class ? LoadKind::kReferrersClass : LoadKind::kDexCacheViaMethod);
     SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
     SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
+    SetPackedFlag<kFlagIsInBootImage>(is_in_boot_image);
     SetPackedFlag<kFlagGenerateClInitCheck>(false);
   }
 
@@ -5565,6 +5567,7 @@
   bool IsReferrersClass() const { return GetLoadKind() == LoadKind::kReferrersClass; }
   bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); }
   bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
+  bool IsInBootImage() const { return GetPackedFlag<kFlagIsInBootImage>(); }
   bool MustGenerateClinitCheck() const { return GetPackedFlag<kFlagGenerateClInitCheck>(); }
 
   void MarkInDexCache() {
@@ -5574,6 +5577,10 @@
     SetSideEffects(SideEffects::None());
   }
 
+  void MarkInBootImage() {
+    SetPackedFlag<kFlagIsInBootImage>(true);
+  }
+
   void AddSpecialInput(HInstruction* special_input);
 
   using HInstruction::GetInputRecords;  // Keep the const version visible.
@@ -5591,9 +5598,10 @@
  private:
   static constexpr size_t kFlagNeedsAccessCheck    = kNumberOfGenericPackedBits;
   static constexpr size_t kFlagIsInDexCache        = kFlagNeedsAccessCheck + 1;
+  static constexpr size_t kFlagIsInBootImage       = kFlagIsInDexCache + 1;
   // Whether this instruction must generate the initialization check.
   // Used for code generation.
-  static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1;
+  static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInBootImage + 1;
   static constexpr size_t kFieldLoadKind           = kFlagGenerateClInitCheck + 1;
   static constexpr size_t kFieldLoadKindSize =
       MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 81163e2..8d4d143 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -176,6 +176,7 @@
   uint32_t type_index = load_class->GetTypeIndex();
 
   bool is_in_dex_cache = false;
+  bool is_in_boot_image = false;
   HLoadClass::LoadKind desired_load_kind;
   uint64_t address = 0u;  // Class or dex cache element address.
   {
@@ -192,45 +193,42 @@
       // Compiling boot image. Check if the class is a boot image class.
       DCHECK(!runtime->UseJitCompilation());
       if (!compiler_driver_->GetSupportBootImageFixup()) {
-        // MIPS/MIPS64 or compiler_driver_test. Do not sharpen.
+        // MIPS64 or compiler_driver_test. Do not sharpen.
         desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
+      } else if ((klass != nullptr) && compiler_driver_->IsImageClass(
+          dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
+        is_in_boot_image = true;
+        is_in_dex_cache = true;
+        desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
+            ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
+            : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
       } else {
-        if (klass != nullptr &&
-            compiler_driver_->IsImageClass(
-                dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
-          is_in_dex_cache = true;
-          desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
-              ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
-              : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
-        } else {
-          // Not a boot image class. We must go through the dex cache.
-          DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
-          desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
-        }
-      }
-    } else if (runtime->UseJitCompilation()) {
-      // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
-      // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
-      is_in_dex_cache = (klass != nullptr);
-      if (klass != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(klass)) {
-        // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
-        desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
-        address = reinterpret_cast64<uint64_t>(klass);
-      } else {
-        // Note: If the class is not in the dex cache or isn't initialized, the
-        // instruction needs environment and will not be inlined across dex files.
-        // Within a dex file, the slow-path helper loads the correct class and
-        // inlined frames are used correctly for OOM stack trace.
-        // TODO: Write a test for this. Bug: 29416588
-        desired_load_kind = HLoadClass::LoadKind::kDexCacheAddress;
-        void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index];
-        address = reinterpret_cast64<uint64_t>(dex_cache_element_address);
+        // Not a boot image class. We must go through the dex cache.
+        DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
+        desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
       }
     } else {
-      // AOT app compilation. Check if the class is in the boot image.
-      if ((klass != nullptr) &&
-          runtime->GetHeap()->ObjectIsInBootImageSpace(klass) &&
-          !codegen_->GetCompilerOptions().GetCompilePic()) {
+      is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass);
+      if (runtime->UseJitCompilation()) {
+        // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
+        // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
+        is_in_dex_cache = (klass != nullptr);
+        if (is_in_boot_image) {
+          // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
+          desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
+          address = reinterpret_cast64<uint64_t>(klass);
+        } else {
+          // Note: If the class is not in the dex cache or isn't initialized, the
+          // instruction needs environment and will not be inlined across dex files.
+          // Within a dex file, the slow-path helper loads the correct class and
+          // inlined frames are used correctly for OOM stack trace.
+          // TODO: Write a test for this. Bug: 29416588
+          desired_load_kind = HLoadClass::LoadKind::kDexCacheAddress;
+          void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index];
+          address = reinterpret_cast64<uint64_t>(dex_cache_element_address);
+        }
+        // AOT app compilation. Check if the class is in the boot image.
+      } else if (is_in_boot_image && !codegen_->GetCompilerOptions().GetCompilePic()) {
         desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
         address = reinterpret_cast64<uint64_t>(klass);
       } else {
@@ -247,6 +245,9 @@
   if (is_in_dex_cache) {
     load_class->MarkInDexCache();
   }
+  if (is_in_boot_image) {
+    load_class->MarkInBootImage();
+  }
 
   HLoadClass::LoadKind load_kind = codegen_->GetSupportedLoadClassKind(desired_load_kind);
   switch (load_kind) {
diff --git a/tools/bisection-search/README.md b/tools/bisection-search/README.md
index 857c930..a7485c2 100644
--- a/tools/bisection-search/README.md
+++ b/tools/bisection-search/README.md
@@ -15,29 +15,29 @@
 How to run Bisection Bug Search
 ===============================
 
-    bisection_search.py [-h] -cp CLASSPATH
-                        [--expected-output EXPECTED_OUTPUT] [--device]
-                        [--lib LIB] [--64]
-                        [--dalvikvm-option [OPTION [OPTION ...]]]
-                        [--arg [TEST_ARGS [TEST_ARGS ...]]] [--image IMAGE]
-                        [--verbose]
-                        classname
+    bisection_search.py [-h] [-cp CLASSPATH] [--class CLASSNAME] [--lib LIB]
+                               [--dalvikvm-option [OPT [OPT ...]]] [--arg [ARG [ARG ...]]]
+                               [--image IMAGE] [--raw-cmd RAW_CMD]
+                               [--64] [--device] [--expected-output EXPECTED_OUTPUT]
+                               [--check-script CHECK_SCRIPT] [--verbose]
 
-    positional arguments:
-      classname             name of class to run
+    Tool for finding compiler bugs. Either --raw-cmd or both -cp and --class are required.
 
     optional arguments:
-      -h, --help            show this help message and exit
-      -cp CLASSPATH, --classpath CLASSPATH
-                            classpath
-      --expected-output EXPECTED_OUTPUT
-                            file containing expected output
-      --device              run on device
-      --lib LIB             lib to use, default: libart.so
-      --64                  x64 mode
-      --dalvikvm-option [OPTION [OPTION ...]]
-                            additional dalvikvm option
-      --arg [TEST_ARGS [TEST_ARGS ...]]
-                            argument to pass to program
-      --image IMAGE         path to image
-      --verbose             enable verbose output
+      -h, --help                            show this help message and exit
+
+    dalvikvm command options:
+      -cp CLASSPATH, --classpath CLASSPATH  classpath
+      --class CLASSNAME                     name of main class
+      --lib LIB                             lib to use, default: libart.so
+      --dalvikvm-option [OPT [OPT ...]]     additional dalvikvm option
+      --arg [ARG [ARG ...]]                 argument passed to test
+      --image IMAGE                         path to image
+      --raw-cmd RAW_CMD                     bisect with this command, ignore other command options
+
+    bisection options:
+      --64                                  x64 mode
+      --device                              run on device
+      --expected-output EXPECTED_OUTPUT     file containing expected output
+      --check-script CHECK_SCRIPT           script comparing output and expected output
+      --verbose                             enable verbose output
diff --git a/tools/bisection-search/bisection_search.py b/tools/bisection-search/bisection_search.py
index d6c1749..110ef82 100755
--- a/tools/bisection-search/bisection_search.py
+++ b/tools/bisection-search/bisection_search.py
@@ -22,15 +22,20 @@
 ./bisection-search.py -cp classes.dex --expected-output output Test
 """
 
+import abc
 import argparse
 import re
+import shlex
+from subprocess import call
 import sys
+from tempfile import NamedTemporaryFile
 
 from common import DeviceTestEnv
 from common import FatalError
 from common import GetEnvVariableOrError
 from common import HostTestEnv
 
+
 # Passes that are never disabled during search process because disabling them
 # would compromise correctness.
 MANDATORY_PASSES = ['dex_cache_array_fixups_arm',
@@ -53,23 +58,18 @@
   Accepts filters on compiled methods and optimization passes.
   """
 
-  def __init__(self, base_cmd, test_env, class_name, args,
-               expected_output=None, verbose=False):
+  def __init__(self, base_cmd, test_env, output_checker=None, verbose=False):
     """Constructor.
 
     Args:
       base_cmd: list of strings, base command to run.
       test_env: ITestEnv.
-      class_name: string, name of class to run.
-      args: list of strings, program arguments to pass.
-      expected_output: string, expected output to compare against or None.
+      output_checker: IOutputCheck, output checker.
       verbose: bool, enable verbose output.
     """
     self._base_cmd = base_cmd
     self._test_env = test_env
-    self._class_name = class_name
-    self._args = args
-    self._expected_output = expected_output
+    self._output_checker = output_checker
     self._compiled_methods_path = self._test_env.CreateFile('compiled_methods')
     self._passes_to_run_path = self._test_env.CreateFile('run_passes')
     self._verbose = verbose
@@ -88,14 +88,15 @@
       True if test passes with given settings. False otherwise.
     """
     if self._verbose:
-      print('Testing methods: {0} passes:{1}.'.format(
+      print('Testing methods: {0} passes: {1}.'.format(
           compiled_methods, passes_to_run))
     cmd = self._PrepareCmd(compiled_methods=compiled_methods,
                            passes_to_run=passes_to_run,
-                           verbose_compiler=True)
-    (output, _, ret_code) = self._test_env.RunCommand(cmd)
-    res = ret_code == 0 and (self._expected_output is None
-                             or output == self._expected_output)
+                           verbose_compiler=False)
+    (output, ret_code) = self._test_env.RunCommand(
+        cmd, {'ANDROID_LOG_TAGS': '*:e'})
+    res = ((self._output_checker is None and ret_code == 0)
+           or self._output_checker.Check(output))
     if self._verbose:
       print('Test passed: {0}.'.format(res))
     return res
@@ -110,8 +111,8 @@
       FatalError: An error occurred when retrieving methods list.
     """
     cmd = self._PrepareCmd(verbose_compiler=True)
-    (_, err_output, _) = self._test_env.RunCommand(cmd)
-    match_methods = re.findall(r'Building ([^\n]+)\n', err_output)
+    (output, _) = self._test_env.RunCommand(cmd, {'ANDROID_LOG_TAGS': '*:i'})
+    match_methods = re.findall(r'Building ([^\n]+)\n', output)
     if not match_methods:
       raise FatalError('Failed to retrieve methods list. '
                        'Not recognized output format.')
@@ -131,8 +132,8 @@
     """
     cmd = self._PrepareCmd(compiled_methods=[compiled_method],
                            verbose_compiler=True)
-    (_, err_output, _) = self._test_env.RunCommand(cmd)
-    match_passes = re.findall(r'Starting pass: ([^\n]+)\n', err_output)
+    (output, _) = self._test_env.RunCommand(cmd, {'ANDROID_LOG_TAGS': '*:i'})
+    match_passes = re.findall(r'Starting pass: ([^\n]+)\n', output)
     if not match_passes:
       raise FatalError('Failed to retrieve passes list. '
                        'Not recognized output format.')
@@ -141,7 +142,8 @@
   def _PrepareCmd(self, compiled_methods=None, passes_to_run=None,
                   verbose_compiler=False):
     """Prepare command to run."""
-    cmd = list(self._base_cmd)
+    cmd = [self._base_cmd[0]]
+    # insert additional arguments
     if compiled_methods is not None:
       self._test_env.WriteLines(self._compiled_methods_path, compiled_methods)
       cmd += ['-Xcompiler-option', '--compiled-methods={0}'.format(
@@ -152,12 +154,78 @@
           self._passes_to_run_path)]
     if verbose_compiler:
       cmd += ['-Xcompiler-option', '--runtime-arg', '-Xcompiler-option',
-              '-verbose:compiler']
-    cmd += ['-classpath', self._test_env.classpath, self._class_name]
-    cmd += self._args
+              '-verbose:compiler', '-Xcompiler-option', '-j1']
+    cmd += self._base_cmd[1:]
     return cmd
 
 
+class IOutputCheck(object):
+  """Abstract output checking class.
+
+  Checks if output is correct.
+  """
+  __meta_class__ = abc.ABCMeta
+
+  @abc.abstractmethod
+  def Check(self, output):
+    """Check if output is correct.
+
+    Args:
+      output: string, output to check.
+
+    Returns:
+      boolean, True if output is correct, False otherwise.
+    """
+
+
+class EqualsOutputCheck(IOutputCheck):
+  """Concrete output checking class checking for equality to expected output."""
+
+  def __init__(self, expected_output):
+    """Constructor.
+
+    Args:
+      expected_output: string, expected output.
+    """
+    self._expected_output = expected_output
+
+  def Check(self, output):
+    """See base class."""
+    return self._expected_output == output
+
+
+class ExternalScriptOutputCheck(IOutputCheck):
+  """Concrete output checking class calling an external script.
+
+  The script should accept two arguments, path to expected output and path to
+  program output. It should exit with 0 return code if outputs are equivalent
+  and with different return code otherwise.
+  """
+
+  def __init__(self, script_path, expected_output_path, logfile):
+    """Constructor.
+
+    Args:
+      script_path: string, path to checking script.
+      expected_output_path: string, path to file with expected output.
+      logfile: file handle, logfile.
+    """
+    self._script_path = script_path
+    self._expected_output_path = expected_output_path
+    self._logfile = logfile
+
+  def Check(self, output):
+    """See base class."""
+    ret_code = None
+    with NamedTemporaryFile(mode='w', delete=False) as temp_file:
+      temp_file.write(output)
+      temp_file.flush()
+      ret_code = call(
+          [self._script_path, self._expected_output_path, temp_file.name],
+          stdout=self._logfile, stderr=self._logfile, universal_newlines=True)
+    return ret_code == 0
+
+
 def BinarySearch(start, end, test):
   """Binary search integers using test function to guide the process."""
   while start < end:
@@ -200,9 +268,9 @@
   all_methods = testable.GetAllMethods()
   faulty_method_idx = BinarySearch(
       0,
-      len(all_methods),
+      len(all_methods) + 1,
       lambda mid: testable.Test(all_methods[0:mid]))
-  if faulty_method_idx == len(all_methods):
+  if faulty_method_idx == len(all_methods) + 1:
     return (None, None)
   if faulty_method_idx == 0:
     raise FatalError('Testable fails with no methods compiled. '
@@ -211,72 +279,100 @@
   all_passes = testable.GetAllPassesForMethod(faulty_method)
   faulty_pass_idx = BinarySearch(
       0,
-      len(all_passes),
+      len(all_passes) + 1,
       lambda mid: testable.Test([faulty_method],
                                 FilterPasses(all_passes, mid)))
   if faulty_pass_idx == 0:
     return (faulty_method, None)
-  assert faulty_pass_idx != len(all_passes), 'Method must fail for some passes.'
+  assert faulty_pass_idx != len(all_passes) + 1, ('Method must fail for some '
+                                                  'passes.')
   faulty_pass = all_passes[faulty_pass_idx - 1]
   return (faulty_method, faulty_pass)
 
 
 def PrepareParser():
   """Prepares argument parser."""
-  parser = argparse.ArgumentParser()
-  parser.add_argument(
-      '-cp', '--classpath', required=True, type=str, help='classpath')
-  parser.add_argument('--expected-output', type=str,
-                      help='file containing expected output')
-  parser.add_argument(
+  parser = argparse.ArgumentParser(
+      description='Tool for finding compiler bugs. Either --raw-cmd or both '
+                  '-cp and --class are required.')
+  command_opts = parser.add_argument_group('dalvikvm command options')
+  command_opts.add_argument('-cp', '--classpath', type=str, help='classpath')
+  command_opts.add_argument('--class', dest='classname', type=str,
+                            help='name of main class')
+  command_opts.add_argument('--lib', dest='lib', type=str, default='libart.so',
+                            help='lib to use, default: libart.so')
+  command_opts.add_argument('--dalvikvm-option', dest='dalvikvm_opts',
+                            metavar='OPT', nargs='*', default=[],
+                            help='additional dalvikvm option')
+  command_opts.add_argument('--arg', dest='test_args', nargs='*', default=[],
+                            metavar='ARG', help='argument passed to test')
+  command_opts.add_argument('--image', type=str, help='path to image')
+  command_opts.add_argument('--raw-cmd', dest='raw_cmd', type=str,
+                            help='bisect with this command, ignore other '
+                                 'command options')
+  bisection_opts = parser.add_argument_group('bisection options')
+  bisection_opts.add_argument('--64', dest='x64', action='store_true',
+                              default=False, help='x64 mode')
+  bisection_opts.add_argument(
       '--device', action='store_true', default=False, help='run on device')
-  parser.add_argument('classname', type=str, help='name of class to run')
-  parser.add_argument('--lib', dest='lib', type=str, default='libart.so',
-                      help='lib to use, default: libart.so')
-  parser.add_argument('--64', dest='x64', action='store_true',
-                      default=False, help='x64 mode')
-  parser.add_argument('--dalvikvm-option', dest='dalvikvm_opts',
-                      metavar='OPTION', nargs='*', default=[],
-                      help='additional dalvikvm option')
-  parser.add_argument('--arg', dest='test_args', nargs='*', default=[],
-                      help='argument to pass to program')
-  parser.add_argument('--image', type=str, help='path to image')
-  parser.add_argument('--verbose', action='store_true',
-                      default=False, help='enable verbose output')
+  bisection_opts.add_argument('--expected-output', type=str,
+                              help='file containing expected output')
+  bisection_opts.add_argument(
+      '--check-script', dest='check_script', type=str,
+      help='script comparing output and expected output')
+  bisection_opts.add_argument('--verbose', action='store_true',
+                              default=False, help='enable verbose output')
   return parser
 
 
+def PrepareBaseCommand(args, classpath):
+  """Prepares base command used to run test."""
+  if args.raw_cmd:
+    return shlex.split(args.raw_cmd)
+  else:
+    base_cmd = ['dalvikvm64'] if args.x64 else ['dalvikvm32']
+    if not args.device:
+      base_cmd += ['-XXlib:{0}'.format(args.lib)]
+      if not args.image:
+        image_path = '{0}/framework/core-optimizing-pic.art'.format(
+            GetEnvVariableOrError('ANDROID_HOST_OUT'))
+      else:
+        image_path = args.image
+      base_cmd += ['-Ximage:{0}'.format(image_path)]
+    if args.dalvikvm_opts:
+      base_cmd += args.dalvikvm_opts
+    base_cmd += ['-cp', classpath, args.classname] + args.test_args
+  return base_cmd
+
+
 def main():
   # Parse arguments
   parser = PrepareParser()
   args = parser.parse_args()
+  if not args.raw_cmd and (not args.classpath or not args.classname):
+    parser.error('Either --raw-cmd or both -cp and --class are required')
 
   # Prepare environment
-  if args.expected_output is not None:
-    with open(args.expected_output, 'r') as f:
-      expected_output = f.read()
-  else:
-    expected_output = None
+  classpath = args.classpath
   if args.device:
-    run_cmd = ['dalvikvm64'] if args.x64 else ['dalvikvm32']
-    test_env = DeviceTestEnv(args.classpath)
+    test_env = DeviceTestEnv()
+    if classpath:
+      classpath = test_env.PushClasspath(classpath)
   else:
-    run_cmd = ['dalvikvm64'] if args.x64 else ['dalvikvm32']
-    run_cmd += ['-XXlib:{0}'.format(args.lib)]
-    if not args.image:
-      image_path = '{0}/framework/core-optimizing-pic.art'.format(
-          GetEnvVariableOrError('ANDROID_HOST_OUT'))
+    test_env = HostTestEnv(args.x64)
+  base_cmd = PrepareBaseCommand(args, classpath)
+  output_checker = None
+  if args.expected_output:
+    if args.check_script:
+      output_checker = ExternalScriptOutputCheck(
+          args.check_script, args.expected_output, test_env.logfile)
     else:
-      image_path = args.image
-    run_cmd += ['-Ximage:{0}'.format(image_path)]
-    if args.dalvikvm_opts:
-      run_cmd += args.dalvikvm_opts
-    test_env = HostTestEnv(args.classpath, args.x64)
+      with open(args.expected_output, 'r') as expected_output_file:
+        output_checker = EqualsOutputCheck(expected_output_file.read())
 
   # Perform the search
   try:
-    testable = Dex2OatWrapperTestable(run_cmd, test_env, args.classname,
-                                      args.test_args, expected_output,
+    testable = Dex2OatWrapperTestable(base_cmd, test_env, output_checker,
                                       args.verbose)
     (method, opt_pass) = BugSearch(testable)
   except Exception as e:
diff --git a/tools/bisection-search/common.py b/tools/bisection-search/common.py
index 8361fc9..d5029bb 100755
--- a/tools/bisection-search/common.py
+++ b/tools/bisection-search/common.py
@@ -23,6 +23,7 @@
 from subprocess import check_call
 from subprocess import PIPE
 from subprocess import Popen
+from subprocess import STDOUT
 from subprocess import TimeoutExpired
 
 from tempfile import mkdtemp
@@ -81,19 +82,20 @@
   Returns:
    tuple (string, string, int) stdout output, stderr output, return code.
   """
-  proc = Popen(cmd, stderr=PIPE, stdout=PIPE, env=env, universal_newlines=True)
+  proc = Popen(cmd, stderr=STDOUT, stdout=PIPE, env=env,
+               universal_newlines=True)
   timeouted = False
   try:
-    (output, err_output) = proc.communicate(timeout=timeout)
+    (output, _) = proc.communicate(timeout=timeout)
   except TimeoutExpired:
     timeouted = True
     proc.kill()
-    (output, err_output) = proc.communicate()
-  logfile.write('Command:\n{0}\n{1}{2}\nReturn code: {3}\n'.format(
-      _CommandListToCommandString(cmd), err_output, output,
+    (output, _) = proc.communicate()
+  logfile.write('Command:\n{0}\n{1}\nReturn code: {2}\n'.format(
+      _CommandListToCommandString(cmd), output,
       'TIMEOUT' if timeouted else proc.returncode))
   ret_code = 1 if timeouted else proc.returncode
-  return (output, err_output, ret_code)
+  return (output, ret_code)
 
 
 def _CommandListToCommandString(cmd):
@@ -148,21 +150,18 @@
     """
 
   @abc.abstractmethod
-  def RunCommand(self, cmd):
-    """Runs command in environment.
+  def RunCommand(self, cmd, env_updates=None):
+    """Runs command in environment with updated environmental variables.
 
     Args:
-      cmd: string, command to run.
-
+      cmd: list of strings, command to run.
+      env_updates: dict, string to string, maps names of variables to their
+        updated values.
     Returns:
       tuple (string, string, int) stdout output, stderr output, return code.
     """
 
   @abc.abstractproperty
-  def classpath(self):
-    """Gets environment specific classpath with test class."""
-
-  @abc.abstractproperty
   def logfile(self):
     """Gets file handle to logfile residing on host."""
 
@@ -176,14 +175,12 @@
   For methods documentation see base class.
   """
 
-  def __init__(self, classpath, x64):
+  def __init__(self, x64):
     """Constructor.
 
     Args:
-      classpath: string, classpath with test class.
       x64: boolean, whether to setup in x64 mode.
     """
-    self._classpath = classpath
     self._env_path = mkdtemp(dir='/tmp/', prefix='bisection_search_')
     self._logfile = open('{0}/log'.format(self._env_path), 'w+')
     os.mkdir('{0}/dalvik-cache'.format(self._env_path))
@@ -197,6 +194,7 @@
     self._shell_env['ANDROID_DATA'] = self._env_path
     self._shell_env['ANDROID_ROOT'] = android_root
     self._shell_env['LD_LIBRARY_PATH'] = library_path
+    self._shell_env['DYLD_LIBRARY_PATH'] = library_path
     self._shell_env['PATH'] = (path + ':' + self._shell_env['PATH'])
     # Using dlopen requires load bias on the host.
     self._shell_env['LD_USE_LOAD_BIAS'] = '1'
@@ -213,13 +211,13 @@
       f.writelines('{0}\n'.format(line) for line in lines)
     return
 
-  def RunCommand(self, cmd):
+  def RunCommand(self, cmd, env_updates=None):
+    if not env_updates:
+      env_updates = {}
     self._EmptyDexCache()
-    return _RunCommandForOutputAndLog(cmd, self._shell_env, self._logfile)
-
-  @property
-  def classpath(self):
-    return self._classpath
+    env = self._shell_env.copy()
+    env.update(env_updates)
+    return _RunCommandForOutputAndLog(cmd, env, self._logfile)
 
   @property
   def logfile(self):
@@ -247,32 +245,18 @@
   For methods documentation see base class.
   """
 
-  def __init__(self, classpath):
-    """Constructor.
-
-    Args:
-      classpath: string, classpath with test class.
-    """
+  def __init__(self):
+    """Constructor."""
     self._host_env_path = mkdtemp(dir='/tmp/', prefix='bisection_search_')
     self._logfile = open('{0}/log'.format(self._host_env_path), 'w+')
     self._device_env_path = '{0}/{1}'.format(
         DEVICE_TMP_PATH, os.path.basename(self._host_env_path))
-    self._classpath = os.path.join(
-        self._device_env_path, os.path.basename(classpath))
-    self._shell_env = os.environ
+    self._shell_env = os.environ.copy()
 
     self._AdbMkdir('{0}/dalvik-cache'.format(self._device_env_path))
     for arch_cache_path in _DexArchCachePaths(self._device_env_path):
       self._AdbMkdir(arch_cache_path)
 
-    paths = classpath.split(':')
-    device_paths = []
-    for path in paths:
-      device_paths.append('{0}/{1}'.format(
-          self._device_env_path, os.path.basename(path)))
-      self._AdbPush(path, self._device_env_path)
-    self._classpath = ':'.join(device_paths)
-
   def CreateFile(self, name=None):
     with NamedTemporaryFile(mode='w') as temp_file:
       self._AdbPush(temp_file.name, self._device_env_path)
@@ -283,25 +267,47 @@
   def WriteLines(self, file_path, lines):
     with NamedTemporaryFile(mode='w') as temp_file:
       temp_file.writelines('{0}\n'.format(line) for line in lines)
+      temp_file.flush()
       self._AdbPush(temp_file.name, file_path)
     return
 
-  def RunCommand(self, cmd):
+  def RunCommand(self, cmd, env_updates=None):
+    if not env_updates:
+      env_updates = {}
     self._EmptyDexCache()
+    if 'ANDROID_DATA' not in env_updates:
+      env_updates['ANDROID_DATA'] = self._device_env_path
+    env_updates_cmd = ' '.join(['{0}={1}'.format(var, val) for var, val
+                                in env_updates.items()])
     cmd = _CommandListToCommandString(cmd)
-    cmd = ('adb shell "logcat -c && ANDROID_DATA={0} {1} && '
-           'logcat -d dex2oat:* *:S 1>&2"').format(self._device_env_path, cmd)
-    return _RunCommandForOutputAndLog(shlex.split(cmd), self._shell_env,
-                                      self._logfile)
-
-  @property
-  def classpath(self):
-    return self._classpath
+    cmd = ('adb shell "logcat -c && {0} {1} ; logcat -d -s dex2oat:* dex2oatd:*'
+           '| grep -v "^---------" 1>&2"').format(env_updates_cmd, cmd)
+    return _RunCommandForOutputAndLog(
+        shlex.split(cmd), self._shell_env, self._logfile)
 
   @property
   def logfile(self):
     return self._logfile
 
+  def PushClasspath(self, classpath):
+    """Push classpath to on-device test directory.
+
+    Classpath can contain multiple colon separated file paths, each file is
+    pushed. Returns analogous classpath with paths valid on device.
+
+    Args:
+      classpath: string, classpath in format 'a/b/c:d/e/f'.
+    Returns:
+      string, classpath valid on device.
+    """
+    paths = classpath.split(':')
+    device_paths = []
+    for path in paths:
+      device_paths.append('{0}/{1}'.format(
+          self._device_env_path, os.path.basename(path)))
+      self._AdbPush(path, self._device_env_path)
+    return ':'.join(device_paths)
+
   def _AdbPush(self, what, where):
     check_call(shlex.split('adb push "{0}" "{1}"'.format(what, where)),
                stdout=self._logfile, stderr=self._logfile)