Merge "Implement cyclic region allocation in ART's region space."
diff --git a/Android.mk b/Android.mk
index 558986e..e4f4e74 100644
--- a/Android.mk
+++ b/Android.mk
@@ -67,6 +67,7 @@
 include $(art_path)/tools/ahat/Android.mk
 include $(art_path)/tools/amm/Android.mk
 include $(art_path)/tools/dexfuzz/Android.mk
+include $(art_path)/tools/veridex/Android.mk
 include $(art_path)/libart_fake/Android.mk
 
 ART_HOST_DEPENDENCIES := \
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index b342abe..b483e5f 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -300,11 +300,13 @@
   $(HOST_CORE_IMAGE_DEFAULT_64) \
   $(HOST_CORE_IMAGE_DEFAULT_32) \
   oatdumpd-host \
-  oatdumpds-host
+  oatdumpds-host \
+  dexdump2-host
 ART_GTEST_oatdump_test_TARGET_DEPS := \
   $(TARGET_CORE_IMAGE_DEFAULT_64) \
   $(TARGET_CORE_IMAGE_DEFAULT_32) \
-  oatdumpd-target
+  oatdumpd-target \
+  dexdump2-target
 ART_GTEST_oatdump_image_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS)
 ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS)
 ART_GTEST_oatdump_app_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) \
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index f582341..c139fcf 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -20,6 +20,7 @@
 #include "base/callee_save_type.h"
 #include "base/enums.h"
 #include "base/leb128.h"
+#include "base/malloc_arena_pool.h"
 #include "class_linker.h"
 #include "common_runtime_test.h"
 #include "dex/code_item_accessors-inl.h"
@@ -67,7 +68,7 @@
       fake_code_.push_back(0x70 | i);
     }
 
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaStack arena_stack(&pool);
     ScopedArenaAllocator allocator(&arena_stack);
     StackMapStream stack_maps(&allocator, kRuntimeISA);
diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc
index 236b5c0..11b0e2b 100644
--- a/compiler/jni/jni_cfi_test.cc
+++ b/compiler/jni/jni_cfi_test.cc
@@ -20,6 +20,7 @@
 #include "arch/instruction_set.h"
 #include "base/arena_allocator.h"
 #include "base/enums.h"
+#include "base/malloc_arena_pool.h"
 #include "cfi_test.h"
 #include "gtest/gtest.h"
 #include "jni/quick/calling_convention.h"
@@ -61,7 +62,7 @@
     const bool is_synchronized = false;
     const char* shorty = "IIFII";
 
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     std::unique_ptr<JniCallingConvention> jni_conv(
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index d001cfe..8cb1998 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -27,6 +27,8 @@
 #include "base/enums.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
+#include "base/malloc_arena_pool.h"
+#include "base/memory_region.h"
 #include "base/utils.h"
 #include "calling_convention.h"
 #include "class_linker.h"
@@ -36,7 +38,6 @@
 #include "driver/compiler_options.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "jni_env_ext.h"
-#include "memory_region.h"
 #include "thread.h"
 #include "utils/arm/managed_register_arm.h"
 #include "utils/arm64/managed_register_arm64.h"
@@ -174,7 +175,7 @@
     }
   }
 
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
 
   // Calling conventions used to iterate over parameters to method
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index 7875517..0056c50 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -21,6 +21,7 @@
 #include "arch/arm/asm_support_arm.h"
 #include "art_method.h"
 #include "base/bit_utils.h"
+#include "base/malloc_arena_pool.h"
 #include "compiled_method.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "linker/linker_patch.h"
@@ -355,7 +356,7 @@
 }
 
 std::vector<uint8_t> Thumb2RelativePatcher::CompileThunk(const ThunkKey& key) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
   arm::ArmVIXLAssembler assembler(&allocator);
 
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index b268204..4bfe99b 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -20,6 +20,7 @@
 #include "arch/arm64/instruction_set_features_arm64.h"
 #include "art_method.h"
 #include "base/bit_utils.h"
+#include "base/malloc_arena_pool.h"
 #include "compiled_method-inl.h"
 #include "driver/compiler_driver.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
@@ -511,7 +512,7 @@
 }
 
 std::vector<uint8_t> Arm64RelativePatcher::CompileThunk(const ThunkKey& key) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
   arm64::Arm64Assembler assembler(&allocator);
 
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 3bd5e14..e1f680f 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -24,12 +24,12 @@
 #include "base/bit_field.h"
 #include "base/bit_utils.h"
 #include "base/enums.h"
+#include "base/memory_region.h"
 #include "dex/string_reference.h"
 #include "dex/type_reference.h"
 #include "globals.h"
 #include "graph_visualizer.h"
 #include "locations.h"
-#include "memory_region.h"
 #include "nodes.h"
 #include "optimizing_compiler_stats.h"
 #include "read_barrier_option.h"
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 4053f55..82d1fda 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6755,8 +6755,8 @@
   return 0;
 }
 
-// Interface case has 3 temps, one for holding the number of interfaces, one for the current
-// interface pointer, one for loading the current interface.
+// Interface case has 2 temps, one for holding the number of interfaces, one for the current
+// interface pointer, the current interface is compared in memory.
 // The other checks have one temp for loading the object's class.
 static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
   if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
@@ -7069,9 +7069,7 @@
   } else {
     locations->SetInAt(1, Location::Any());
   }
-  // Note that TypeCheckSlowPathX86 uses this "temp" register too.
-  locations->AddTemp(Location::RequiresRegister());
-  // When read barriers are enabled, we need an additional temporary register for some cases.
+  // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathX86.
   locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
 }
 
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 496d79d..322b0cf 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -6063,24 +6063,26 @@
   CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
 }
 
-static bool CheckCastTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) {
-  if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
-    // We need a temporary for holding the iftable length.
-    return true;
-  }
-  return kEmitCompilerReadBarrier &&
+// Temp is used for read barrier.
+static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
+  if (kEmitCompilerReadBarrier &&
       !kUseBakerReadBarrier &&
       (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
        type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
-       type_check_kind == TypeCheckKind::kArrayObjectCheck);
+       type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
+    return 1;
+  }
+  return 0;
 }
 
-static bool InstanceOfTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) {
-  return kEmitCompilerReadBarrier &&
-      !kUseBakerReadBarrier &&
-      (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
-       type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
-       type_check_kind == TypeCheckKind::kArrayObjectCheck);
+// Interface case has 2 temps, one for holding the number of interfaces, one for the current
+// interface pointer, the current interface is compared in memory.
+// The other checks have one temp for loading the object's class.
+static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
+  if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
+    return 2;
+  }
+  return 1 + NumberOfInstanceOfTemps(type_check_kind);
 }
 
 void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {
@@ -6121,11 +6123,7 @@
   }
   // Note that TypeCheckSlowPathX86_64 uses this "out" register too.
   locations->SetOut(Location::RequiresRegister());
-  // When read barriers are enabled, we need a temporary register for
-  // some cases.
-  if (InstanceOfTypeCheckNeedsATemporary(type_check_kind)) {
-    locations->AddTemp(Location::RequiresRegister());
-  }
+  locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
 }
 
 void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
@@ -6136,9 +6134,9 @@
   Location cls = locations->InAt(1);
   Location out_loc =  locations->Out();
   CpuRegister out = out_loc.AsRegister<CpuRegister>();
-  Location maybe_temp_loc = InstanceOfTypeCheckNeedsATemporary(type_check_kind) ?
-      locations->GetTemp(0) :
-      Location::NoLocation();
+  const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
+  DCHECK_LE(num_temps, 1u);
+  Location maybe_temp_loc = (num_temps >= 1u) ? locations->GetTemp(0) : Location::NoLocation();
   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
   uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
   uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
@@ -6401,14 +6399,8 @@
   } else {
     locations->SetInAt(1, Location::Any());
   }
-
-  // Note that TypeCheckSlowPathX86_64 uses this "temp" register too.
-  locations->AddTemp(Location::RequiresRegister());
-  // When read barriers are enabled, we need an additional temporary
-  // register for some cases.
-  if (CheckCastTypeCheckNeedsATemporary(type_check_kind)) {
-    locations->AddTemp(Location::RequiresRegister());
-  }
+  // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathX86.
+  locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
 }
 
 void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
@@ -6419,9 +6411,10 @@
   Location cls = locations->InAt(1);
   Location temp_loc = locations->GetTemp(0);
   CpuRegister temp = temp_loc.AsRegister<CpuRegister>();
-  Location maybe_temp2_loc = CheckCastTypeCheckNeedsATemporary(type_check_kind) ?
-      locations->GetTemp(1) :
-      Location::NoLocation();
+  const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
+  DCHECK_GE(num_temps, 1u);
+  DCHECK_LE(num_temps, 2u);
+  Location maybe_temp2_loc = (num_temps >= 2u) ? locations->GetTemp(1) : Location::NoLocation();
   const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
   const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
   const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h
index 4a6c914..be26e67 100644
--- a/compiler/optimizing/data_type.h
+++ b/compiler/optimizing/data_type.h
@@ -210,6 +210,12 @@
   static bool IsTypeConversionImplicit(Type input_type, Type result_type);
   static bool IsTypeConversionImplicit(int64_t value, Type result_type);
 
+  static bool IsZeroExtension(Type input_type, Type result_type) {
+    return IsIntOrLongType(result_type) &&
+        IsUnsignedType(input_type) &&
+        Size(result_type) > Size(input_type);
+  }
+
   static const char* PrettyDescriptor(Type type);
 
  private:
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 676fe6b..d3cf956 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -67,7 +67,6 @@
   bool TryCombineVecMultiplyAccumulate(HVecMul* mul);
 
   void VisitShift(HBinaryOperation* shift);
-
   void VisitEqual(HEqual* equal) OVERRIDE;
   void VisitNotEqual(HNotEqual* equal) OVERRIDE;
   void VisitBooleanNot(HBooleanNot* bool_not) OVERRIDE;
@@ -78,6 +77,7 @@
   void VisitNullCheck(HNullCheck* instruction) OVERRIDE;
   void VisitArrayLength(HArrayLength* instruction) OVERRIDE;
   void VisitCheckCast(HCheckCast* instruction) OVERRIDE;
+  void VisitAbs(HAbs* instruction) OVERRIDE;
   void VisitAdd(HAdd* instruction) OVERRIDE;
   void VisitAnd(HAnd* instruction) OVERRIDE;
   void VisitCondition(HCondition* instruction) OVERRIDE;
@@ -903,6 +903,30 @@
           to_type == DataType::Type::kInt64);
 }
 
+// Returns an acceptable substitution for "a" on the select
+// construct "a <cmp> b ? c : .."  during MIN/MAX recognition.
+static HInstruction* AllowInMinMax(IfCondition cmp,
+                                   HInstruction* a,
+                                   HInstruction* b,
+                                   HInstruction* c) {
+  int64_t value = 0;
+  if (IsInt64AndGet(b, /*out*/ &value) &&
+      (((cmp == kCondLT || cmp == kCondLE) && c->IsMax()) ||
+       ((cmp == kCondGT || cmp == kCondGE) && c->IsMin()))) {
+    HConstant* other = c->AsBinaryOperation()->GetConstantRight();
+    if (other != nullptr && a == c->AsBinaryOperation()->GetLeastConstantLeft()) {
+      int64_t other_value = Int64FromConstant(other);
+      bool is_max = (cmp == kCondLT || cmp == kCondLE);
+      // Allow the max for a <  100 ? max(a, -100) : ..
+      //    or the min for a > -100 ? min(a,  100) : ..
+      if (is_max ? (value >= other_value) : (value <= other_value)) {
+        return c;
+      }
+    }
+  }
+  return nullptr;
+}
+
 void InstructionSimplifierVisitor::VisitSelect(HSelect* select) {
   HInstruction* replace_with = nullptr;
   HInstruction* condition = select->GetCondition();
@@ -946,9 +970,17 @@
     DataType::Type t_type = true_value->GetType();
     DataType::Type f_type = false_value->GetType();
     // Here we have a <cmp> b ? true_value : false_value.
-    // Test if both values are compatible integral types (resulting
-    // MIN/MAX/ABS type will be int or long, like the condition).
+    // Test if both values are compatible integral types (resulting MIN/MAX/ABS
+    // type will be int or long, like the condition). Replacements are general,
+    // but assume conditions prefer constants on the right.
     if (DataType::IsIntegralType(t_type) && DataType::Kind(t_type) == DataType::Kind(f_type)) {
+      // Allow a <  100 ? max(a, -100) : ..
+      //    or a > -100 ? min(a,  100) : ..
+      // to use min/max instead of a to detect nested min/max expressions.
+      HInstruction* new_a = AllowInMinMax(cmp, a, b, true_value);
+      if (new_a != nullptr) {
+        a = new_a;
+      }
       // Try to replace typical integral MIN/MAX/ABS constructs.
       if ((cmp == kCondLT || cmp == kCondLE || cmp == kCondGT || cmp == kCondGE) &&
           ((a == true_value && b == false_value) ||
@@ -957,19 +989,16 @@
         //    or a > b ? a : b (MAX) or a > b ? b : a (MIN).
         bool is_min = (cmp == kCondLT || cmp == kCondLE) == (a == true_value);
         replace_with = NewIntegralMinMax(GetGraph()->GetAllocator(), a, b, select, is_min);
-      } else if (true_value->IsNeg()) {
-        HInstruction* negated = true_value->InputAt(0);
-        if ((cmp == kCondLT || cmp == kCondLE) &&
-            (a == negated && a == false_value && IsInt64Value(b, 0))) {
-          // Found a < 0 ? -a : a which can be replaced by ABS(a).
-          replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), false_value, select);
-        }
-      } else if (false_value->IsNeg()) {
-        HInstruction* negated = false_value->InputAt(0);
-        if ((cmp == kCondGT || cmp == kCondGE) &&
-            (a == true_value && a == negated && IsInt64Value(b, 0))) {
-          // Found a > 0 ? a : -a which can be replaced by ABS(a).
-          replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select);
+      } else if (((cmp == kCondLT || cmp == kCondLE) && true_value->IsNeg()) ||
+                 ((cmp == kCondGT || cmp == kCondGE) && false_value->IsNeg())) {
+        bool negLeft = (cmp == kCondLT || cmp == kCondLE);
+        HInstruction* the_negated = negLeft ? true_value->InputAt(0) : false_value->InputAt(0);
+        HInstruction* not_negated = negLeft ? false_value : true_value;
+        if (a == the_negated && a == not_negated && IsInt64Value(b, 0)) {
+          // Found a < 0 ? -a :  a
+          //    or a > 0 ?  a : -a
+          // which can be replaced by ABS(a).
+          replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), a, select);
         }
       } else if (true_value->IsSub() && false_value->IsSub()) {
         HInstruction* true_sub1 = true_value->InputAt(0);
@@ -981,8 +1010,8 @@
              ((cmp == kCondLT || cmp == kCondLE) &&
               (a == true_sub2 && b == true_sub1 && a == false_sub1 && b == false_sub2))) &&
             AreLowerPrecisionArgs(t_type, a, b)) {
-          // Found a > b ? a - b  : b - a   or
-          //       a < b ? b - a  : a - b
+          // Found a > b ? a - b  : b - a
+          //    or a < b ? b - a  : a - b
           // which can be replaced by ABS(a - b) for lower precision operands a, b.
           replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select);
         }
@@ -1241,6 +1270,17 @@
   }
 }
 
+void InstructionSimplifierVisitor::VisitAbs(HAbs* instruction) {
+  HInstruction* input = instruction->GetInput();
+  if (DataType::IsZeroExtension(input->GetType(), instruction->GetResultType())) {
+    // Zero extension from narrow to wide can never set sign bit in the wider
+    // operand, making the subsequent Abs redundant (e.g., abs(b & 0xff) for byte b).
+    instruction->ReplaceWith(input);
+    instruction->GetBlock()->RemoveInstruction(instruction);
+    RecordSimplification();
+  }
+}
+
 void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) {
   HConstant* input_cst = instruction->GetConstantRight();
   HInstruction* input_other = instruction->GetLeastConstantLeft();
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 81c0b509..c3d643a 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2875,6 +2875,14 @@
   __ Bind(&done);
 }
 
+void IntrinsicLocationsBuilderARM64::VisitReachabilityFence(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+  locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorARM64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+
 UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
 
 UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index e61a0b0..29aecbc 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -3028,6 +3028,14 @@
   }
 }
 
+void IntrinsicLocationsBuilderARMVIXL::VisitReachabilityFence(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+  locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble)   // Could be done by changing rounding mode, maybe?
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong)     // High register pressure.
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index bc1292b..ae248a3 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2693,6 +2693,14 @@
   __ Bind(&done);
 }
 
+void IntrinsicLocationsBuilderMIPS::VisitReachabilityFence(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+  locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+
 // Unimplemented intrinsics.
 
 UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index f429afd..9a9ae71 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2354,6 +2354,14 @@
   __ Bind(&done);
 }
 
+void IntrinsicLocationsBuilderMIPS64::VisitReachabilityFence(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+  locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+
 UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
 
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index c4f322b..f84a33b 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2928,6 +2928,13 @@
   __ Bind(&done);
 }
 
+void IntrinsicLocationsBuilderX86::VisitReachabilityFence(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+  locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorX86::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
 
 UNIMPLEMENTED_INTRINSIC(X86, MathRoundDouble)
 UNIMPLEMENTED_INTRINSIC(X86, ReferenceGetReferent)
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 437bc3d..7627dc9 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2737,6 +2737,14 @@
   __ Bind(&done);
 }
 
+void IntrinsicLocationsBuilderX86_64::VisitReachabilityFence(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+  locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+
 UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite)
 UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite)
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 1d83815..b41b659 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -153,6 +153,18 @@
         return false;
     }
   }
+  // A MIN-MAX on narrower operands qualifies as well
+  // (returning the operator itself).
+  if (instruction->IsMin() || instruction->IsMax()) {
+    HBinaryOperation* min_max = instruction->AsBinaryOperation();
+    DCHECK(min_max->GetType() == DataType::Type::kInt32 ||
+           min_max->GetType() == DataType::Type::kInt64);
+    if (IsSignExtensionAndGet(min_max->InputAt(0), type, operand) &&
+        IsSignExtensionAndGet(min_max->InputAt(1), type, operand)) {
+      *operand = min_max;
+      return true;
+    }
+  }
   return false;
 }
 
@@ -216,6 +228,18 @@
         return false;
     }
   }
+  // A MIN-MAX on narrower operands qualifies as well
+  // (returning the operator itself).
+  if (instruction->IsMin() || instruction->IsMax()) {
+    HBinaryOperation* min_max = instruction->AsBinaryOperation();
+    DCHECK(min_max->GetType() == DataType::Type::kInt32 ||
+           min_max->GetType() == DataType::Type::kInt64);
+    if (IsZeroExtensionAndGet(min_max->InputAt(0), type, operand) &&
+        IsZeroExtensionAndGet(min_max->InputAt(1), type, operand)) {
+      *operand = min_max;
+      return true;
+    }
+  }
   return false;
 }
 
@@ -227,6 +251,7 @@
                                /*out*/ HInstruction** r,
                                /*out*/ HInstruction** s,
                                /*out*/ bool* is_unsigned) {
+  DCHECK(a != nullptr && b != nullptr);
   // Look for a matching sign extension.
   DataType::Type stype = HVecOperation::ToSignedType(type);
   if (IsSignExtensionAndGet(a, stype, r) && IsSignExtensionAndGet(b, stype, s)) {
@@ -247,6 +272,7 @@
                               DataType::Type type,
                               /*out*/ HInstruction** r,
                               /*out*/ bool* is_unsigned) {
+  DCHECK(a != nullptr);
   // Look for a matching sign extension.
   DataType::Type stype = HVecOperation::ToSignedType(type);
   if (IsSignExtensionAndGet(a, stype, r)) {
@@ -270,20 +296,28 @@
   return vl >> (DataType::SizeShift(other_type) - DataType::SizeShift(vector_type));
 }
 
-// Detect up to two instructions a and b, and an acccumulated constant c.
-static bool IsAddConstHelper(HInstruction* instruction,
-                             /*out*/ HInstruction** a,
-                             /*out*/ HInstruction** b,
-                             /*out*/ int64_t* c,
-                             int32_t depth) {
-  static constexpr int32_t kMaxDepth = 8;  // don't search too deep
+// Detect up to two added operands a and b and an acccumulated constant c.
+static bool IsAddConst(HInstruction* instruction,
+                       /*out*/ HInstruction** a,
+                       /*out*/ HInstruction** b,
+                       /*out*/ int64_t* c,
+                       int32_t depth = 8) {  // don't search too deep
   int64_t value = 0;
+  // Enter add/sub while still within reasonable depth.
+  if (depth > 0) {
+    if (instruction->IsAdd()) {
+      return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1) &&
+             IsAddConst(instruction->InputAt(1), a, b, c, depth - 1);
+    } else if (instruction->IsSub() &&
+               IsInt64AndGet(instruction->InputAt(1), &value)) {
+      *c -= value;
+      return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1);
+    }
+  }
+  // Otherwise, deal with leaf nodes.
   if (IsInt64AndGet(instruction, &value)) {
     *c += value;
     return true;
-  } else if (instruction->IsAdd() && depth <= kMaxDepth) {
-    return IsAddConstHelper(instruction->InputAt(0), a, b, c, depth + 1) &&
-           IsAddConstHelper(instruction->InputAt(1), a, b, c, depth + 1);
   } else if (*a == nullptr) {
     *a = instruction;
     return true;
@@ -291,42 +325,40 @@
     *b = instruction;
     return true;
   }
-  return false;  // too many non-const operands
+  return false;  // too many operands
 }
 
-// Detect a + b + c for an optional constant c.
-static bool IsAddConst(HInstruction* instruction,
-                       /*out*/ HInstruction** a,
-                       /*out*/ HInstruction** b,
-                       /*out*/ int64_t* c) {
-  if (instruction->IsAdd()) {
-    // Try to find a + b and accumulated c.
-    if (IsAddConstHelper(instruction->InputAt(0), a, b, c, /*depth*/ 0) &&
-        IsAddConstHelper(instruction->InputAt(1), a, b, c, /*depth*/ 0) &&
-        *b != nullptr) {
-      return true;
+// Detect a + b + c with optional constant c.
+static bool IsAddConst2(HGraph* graph,
+                        HInstruction* instruction,
+                        /*out*/ HInstruction** a,
+                        /*out*/ HInstruction** b,
+                        /*out*/ int64_t* c) {
+  if (IsAddConst(instruction, a, b, c) && *a != nullptr) {
+    if (*b == nullptr) {
+      // Constant is usually already present, unless accumulated.
+      *b = graph->GetConstant(instruction->GetType(), (*c));
+      *c = 0;
     }
-    // Found a + b.
-    *a = instruction->InputAt(0);
-    *b = instruction->InputAt(1);
-    *c = 0;
     return true;
   }
   return false;
 }
 
-// Detect a + c for constant c.
-static bool IsAddConst(HInstruction* instruction,
-                       /*out*/ HInstruction** a,
-                       /*out*/ int64_t* c) {
-  if (instruction->IsAdd()) {
-    if (IsInt64AndGet(instruction->InputAt(0), c)) {
-      *a = instruction->InputAt(1);
-      return true;
-    } else if (IsInt64AndGet(instruction->InputAt(1), c)) {
-      *a = instruction->InputAt(0);
-      return true;
-    }
+// Detect a direct a - b or a hidden a - (-c).
+static bool IsSubConst2(HGraph* graph,
+                        HInstruction* instruction,
+                        /*out*/ HInstruction** a,
+                        /*out*/ HInstruction** b) {
+  int64_t c = 0;
+  if (instruction->IsSub()) {
+    *a = instruction->InputAt(0);
+    *b = instruction->InputAt(1);
+    return true;
+  } else if (IsAddConst(instruction, a, b, &c) && *a != nullptr && *b == nullptr) {
+    // Constant for the hidden subtraction.
+    *b = graph->GetConstant(instruction->GetType(), -c);
+    return true;
   }
   return false;
 }
@@ -378,7 +410,8 @@
 }
 
 // Accept various saturated addition forms.
-static bool IsSaturatedAdd(HInstruction* clippee,
+static bool IsSaturatedAdd(HInstruction* a,
+                           HInstruction* b,
                            DataType::Type type,
                            int64_t lo,
                            int64_t hi,
@@ -390,8 +423,7 @@
   // Tighten the range for signed single clipping on constant.
   if (!is_unsigned) {
     int64_t c = 0;
-    HInstruction* notused = nullptr;
-    if (IsAddConst(clippee, &notused, &c)) {
+    if (IsInt64AndGet(a, &c) || IsInt64AndGet(b, &c)) {
       // For c in proper range and narrower operand r:
       //    MIN(r + c,  127) c > 0
       // or MAX(r + c, -128) c < 0 (and possibly redundant bound).
@@ -413,7 +445,7 @@
 }
 
 // Accept various saturated subtraction forms.
-static bool IsSaturatedSub(HInstruction* clippee,
+static bool IsSaturatedSub(HInstruction* a,
                            DataType::Type type,
                            int64_t lo,
                            int64_t hi,
@@ -425,7 +457,7 @@
   // Tighten the range for signed single clipping on constant.
   if (!is_unsigned) {
     int64_t c = 0;
-    if (IsInt64AndGet(clippee->InputAt(0), /*out*/ &c)) {
+    if (IsInt64AndGet(a, /*out*/ &c)) {
       // For c in proper range and narrower operand r:
       //    MIN(c - r,  127) c > 0
       // or MAX(c - r, -128) c < 0 (and possibly redundant bound).
@@ -1521,8 +1553,7 @@
       return false;  // reject, unless all operands are same-extension narrower
     }
     // Accept MIN/MAX(x, y) for vectorizable operands.
-    DCHECK(r != nullptr);
-    DCHECK(s != nullptr);
+    DCHECK(r != nullptr && s != nullptr);
     if (generate_code && vector_mode_ != kVector) {  // de-idiom
       r = opa;
       s = opb;
@@ -2026,31 +2057,37 @@
       instruction->GetType() != DataType::Type::kInt64) {
     return false;
   }
-  // Clipped addition or subtraction?
+  // Clipped addition or subtraction on narrower operands? We will try both
+  // formats since, e.g., x+c can be interpreted as x+c and x-(-c), depending
+  // on what clipping values are used, to get most benefits.
   int64_t lo = std::numeric_limits<int64_t>::min();
   int64_t hi = std::numeric_limits<int64_t>::max();
   HInstruction* clippee = FindClippee(instruction, &lo, &hi);
-  bool is_add = true;
-  if (clippee->IsAdd()) {
-    is_add = true;
-  } else if (clippee->IsSub()) {
-    is_add = false;
-  } else {
-    return false;  // clippee is not add/sub
-  }
-  // Addition or subtraction on narrower operands?
+  HInstruction* a = nullptr;
+  HInstruction* b = nullptr;
   HInstruction* r = nullptr;
   HInstruction* s = nullptr;
   bool is_unsigned = false;
-  if (IsNarrowerOperands(clippee->InputAt(0), clippee->InputAt(1), type, &r, &s, &is_unsigned) &&
-      (is_add ? IsSaturatedAdd(clippee, type, lo, hi, is_unsigned)
-              : IsSaturatedSub(clippee, type, lo, hi, is_unsigned))) {
-    DCHECK(r != nullptr);
-    DCHECK(s != nullptr);
+  bool is_add = true;
+  int64_t c = 0;
+  // First try for saturated addition.
+  if (IsAddConst2(graph_, clippee, /*out*/ &a, /*out*/ &b, /*out*/ &c) && c == 0 &&
+      IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) &&
+      IsSaturatedAdd(r, s, type, lo, hi, is_unsigned)) {
+    is_add = true;
   } else {
-    return false;
+    // Then try again for saturated subtraction.
+    a = b = r = s = nullptr;
+    if (IsSubConst2(graph_, clippee, /*out*/ &a, /*out*/ &b) &&
+        IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) &&
+        IsSaturatedSub(r, type, lo, hi, is_unsigned)) {
+      is_add = false;
+    } else {
+      return false;
+    }
   }
   // Accept saturation idiom for vectorizable operands.
+  DCHECK(r != nullptr && s != nullptr);
   if (generate_code && vector_mode_ != kVector) {  // de-idiom
     r = instruction->InputAt(0);
     s = instruction->InputAt(1);
@@ -2101,8 +2138,7 @@
     HInstruction* a = nullptr;
     HInstruction* b = nullptr;
     int64_t       c = 0;
-    if (IsAddConst(instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) {
-      DCHECK(a != nullptr && b != nullptr);
+    if (IsAddConst2(graph_, instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) {
       // Accept c == 1 (rounded) or c == 0 (not rounded).
       bool is_rounded = false;
       if (c == 1) {
@@ -2124,8 +2160,7 @@
       }
       // Accept recognized halving add for vectorizable operands. Vectorized code uses the
       // shorthand idiomatic operation. Sequential code uses the original scalar expressions.
-      DCHECK(r != nullptr);
-      DCHECK(s != nullptr);
+      DCHECK(r != nullptr && s != nullptr);
       if (generate_code && vector_mode_ != kVector) {  // de-idiom
         r = instruction->InputAt(0);
         s = instruction->InputAt(1);
@@ -2175,19 +2210,11 @@
   HInstruction* v = instruction->InputAt(1);
   HInstruction* a = nullptr;
   HInstruction* b = nullptr;
-  if (v->GetType() == reduction_type && v->IsAbs()) {
-    HInstruction* x = v->InputAt(0);
-    if (x->GetType() == reduction_type) {
-      int64_t c = 0;
-      if (x->IsSub()) {
-        a = x->InputAt(0);
-        b = x->InputAt(1);
-      } else if (IsAddConst(x, /*out*/ &a, /*out*/ &c)) {
-        b = graph_->GetConstant(reduction_type, -c);  // hidden SUB!
-      }
-    }
-  }
-  if (a == nullptr || b == nullptr) {
+  if (v->IsAbs() &&
+      v->GetType() == reduction_type &&
+      IsSubConst2(graph_, v->InputAt(0), /*out*/ &a, /*out*/ &b)) {
+    DCHECK(a != nullptr && b != nullptr);
+  } else {
     return false;
   }
   // Accept same-type or consistent sign extension for narrower-type on operands a and b.
@@ -2220,8 +2247,7 @@
   }
   // Accept SAD idiom for vectorizable operands. Vectorized code uses the shorthand
   // idiomatic operation. Sequential code uses the original scalar expressions.
-  DCHECK(r != nullptr);
-  DCHECK(s != nullptr);
+  DCHECK(r != nullptr && s != nullptr);
   if (generate_code && vector_mode_ != kVector) {  // de-idiom
     r = s = v->InputAt(0);
   }
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index e0a9cfb..9a26f2f 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -125,11 +125,6 @@
   }
 
   void Log() const {
-    if (!kIsDebugBuild && !VLOG_IS_ON(compiler)) {
-      // Log only in debug builds or if the compiler is verbose.
-      return;
-    }
-
     uint32_t compiled_intrinsics = GetStat(MethodCompilationStat::kCompiledIntrinsic);
     uint32_t compiled_native_stubs = GetStat(MethodCompilationStat::kCompiledNativeStub);
     uint32_t bytecode_attempts =
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 6dcbadb..a9bc566 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <vector>
 
+#include "base/malloc_arena_pool.h"
 #include "base/scoped_arena_allocator.h"
 #include "builder.h"
 #include "common_compiler_test.h"
@@ -97,7 +98,7 @@
   ScopedArenaAllocator* GetScopedAllocator() { return &scoped_allocator_; }
 
  private:
-  ArenaPool pool_;
+  MallocArenaPool pool_;
   ArenaAllocator allocator_;
   ArenaStack arena_stack_;
   ScopedArenaAllocator scoped_allocator_;
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index cb87cab..be35201 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -15,6 +15,7 @@
  */
 
 #include "base/arena_allocator.h"
+#include "base/malloc_arena_pool.h"
 #include "nodes.h"
 #include "parallel_move_resolver.h"
 
@@ -180,7 +181,7 @@
 
 
 TYPED_TEST(ParallelMoveTest, Dependency) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
 
   {
@@ -207,7 +208,7 @@
 }
 
 TYPED_TEST(ParallelMoveTest, Cycle) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
 
   {
@@ -257,7 +258,7 @@
 }
 
 TYPED_TEST(ParallelMoveTest, ConstantLast) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
   TypeParam resolver(&allocator);
   HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
@@ -276,7 +277,7 @@
 }
 
 TYPED_TEST(ParallelMoveTest, Pairs) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
 
   {
@@ -453,7 +454,7 @@
 }
 
 TYPED_TEST(ParallelMoveTest, MultiCycles) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
 
   {
@@ -551,7 +552,7 @@
 
 // Test that we do 64bits moves before 32bits moves.
 TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
 
   {
@@ -610,7 +611,7 @@
 }
 
 TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves2) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
 
   {
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
index 66e5142..3f52bdd 100644
--- a/compiler/optimizing/select_generator.cc
+++ b/compiler/optimizing/select_generator.cc
@@ -43,12 +43,16 @@
   for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
     HInstruction* instruction = it.Current();
     if (instruction->IsControlFlow()) {
-      if (num_instructions > kMaxInstructionsInBranch) {
-        return false;
-      }
       return instruction->IsGoto() || instruction->IsReturn();
     } else if (instruction->CanBeMoved() && !instruction->HasSideEffects()) {
-      num_instructions++;
+      if (instruction->IsSelect() &&
+          instruction->AsSelect()->GetCondition()->GetBlock() == block) {
+        // Count one HCondition and HSelect in the same block as a single instruction.
+        // This enables finding nested selects.
+        continue;
+      } else if (++num_instructions > kMaxInstructionsInBranch) {
+        return false;  // bail as soon as we exceed number of allowed instructions
+      }
     } else {
       return false;
     }
@@ -97,6 +101,7 @@
     HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
     HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
     DCHECK_NE(true_block, false_block);
+
     if (!IsSimpleBlock(true_block) ||
         !IsSimpleBlock(false_block) ||
         !BlocksMergeTogether(true_block, false_block)) {
@@ -107,10 +112,10 @@
     // If the branches are not empty, move instructions in front of the If.
     // TODO(dbrazdil): This puts an instruction between If and its condition.
     //                 Implement moving of conditions to first users if possible.
-    if (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) {
+    while (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) {
       true_block->GetFirstInstruction()->MoveBefore(if_instruction);
     }
-    if (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) {
+    while (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) {
       false_block->GetFirstInstruction()->MoveBefore(if_instruction);
     }
     DCHECK(true_block->IsSingleGoto() || true_block->IsSingleReturn());
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 579aabd..268e9bd 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -19,9 +19,9 @@
 
 #include "base/bit_vector-inl.h"
 #include "base/hash_map.h"
+#include "base/memory_region.h"
 #include "base/scoped_arena_containers.h"
 #include "base/value_object.h"
-#include "memory_region.h"
 #include "method_info.h"
 #include "nodes.h"
 #include "stack_map.h"
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 7e517f3..e36c592 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -18,6 +18,7 @@
 
 #include "art_method.h"
 #include "base/arena_bit_vector.h"
+#include "base/malloc_arena_pool.h"
 #include "stack_map_stream.h"
 
 #include "gtest/gtest.h"
@@ -46,7 +47,7 @@
 using Kind = DexRegisterLocation::Kind;
 
 TEST(StackMapTest, Test1) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
   StackMapStream stream(&allocator, kRuntimeISA);
@@ -128,7 +129,7 @@
 }
 
 TEST(StackMapTest, Test2) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
   StackMapStream stream(&allocator, kRuntimeISA);
@@ -413,7 +414,7 @@
 }
 
 TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
   StackMapStream stream(&allocator, kRuntimeISA);
@@ -508,7 +509,7 @@
 }
 
 TEST(StackMapTest, TestNonLiveDexRegisters) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
   StackMapStream stream(&allocator, kRuntimeISA);
@@ -588,7 +589,7 @@
 // StackMap::kNoDexRegisterMapSmallEncoding, and ensure we do
 // not treat it as kNoDexRegisterMap.
 TEST(StackMapTest, DexRegisterMapOffsetOverflow) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
   StackMapStream stream(&allocator, kRuntimeISA);
@@ -652,7 +653,7 @@
 }
 
 TEST(StackMapTest, TestShareDexRegisterMap) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
   StackMapStream stream(&allocator, kRuntimeISA);
@@ -711,7 +712,7 @@
 }
 
 TEST(StackMapTest, TestNoDexRegisterMap) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
   StackMapStream stream(&allocator, kRuntimeISA);
@@ -761,7 +762,7 @@
 }
 
 TEST(StackMapTest, InlineTest) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
   StackMapStream stream(&allocator, kRuntimeISA);
@@ -949,7 +950,7 @@
 }
 
 TEST(StackMapTest, TestDeduplicateStackMask) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
   StackMapStream stream(&allocator, kRuntimeISA);
@@ -978,7 +979,7 @@
 }
 
 TEST(StackMapTest, TestInvokeInfo) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
   StackMapStream stream(&allocator, kRuntimeISA);
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index 921d401..57360e7 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -17,6 +17,7 @@
 #include "trampoline_compiler.h"
 
 #include "base/arena_allocator.h"
+#include "base/malloc_arena_pool.h"
 #include "jni_env_ext.h"
 
 #ifdef ART_ENABLE_CODEGEN_arm
@@ -243,7 +244,7 @@
 std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline64(InstructionSet isa,
                                                                EntryPointCallingConvention abi,
                                                                ThreadOffset64 offset) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
   switch (isa) {
 #ifdef ART_ENABLE_CODEGEN_arm64
@@ -269,7 +270,7 @@
 std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline32(InstructionSet isa,
                                                                EntryPointCallingConvention abi,
                                                                ThreadOffset32 offset) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
   switch (isa) {
 #ifdef ART_ENABLE_CODEGEN_arm
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index 944c64b..421c1b6 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -20,8 +20,8 @@
 #include <vector>
 
 #include "base/casts.h"
+#include "base/memory_region.h"
 #include "globals.h"
-#include "memory_region.h"
 
 namespace art {
 
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index 5b0cd6b..379a639 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -29,10 +29,10 @@
 #include "base/array_ref.h"
 #include "base/enums.h"
 #include "base/macros.h"
+#include "base/memory_region.h"
 #include "debug/dwarf/debug_frame_opcode_writer.h"
 #include "label.h"
 #include "managed_register.h"
-#include "memory_region.h"
 #include "mips/constants_mips.h"
 #include "offsets.h"
 #include "x86/constants_x86.h"
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 0cb8bbb..7c800b3 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -26,6 +26,7 @@
 #include <fstream>
 #include <iterator>
 
+#include "base/malloc_arena_pool.h"
 #include "assembler_test_base.h"
 #include "common_runtime_test.h"  // For ScratchFile
 
@@ -1606,7 +1607,7 @@
 
   static constexpr size_t kWarnManyCombinationsThreshold = 500;
 
-  ArenaPool pool_;
+  MallocArenaPool pool_;
   std::unique_ptr<ArenaAllocator> allocator_;
   std::unique_ptr<Ass> assembler_;
   std::unique_ptr<AssemblerTestInfrastructure> test_helper_;
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 655d17d..053e202 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -27,6 +27,7 @@
 #include "utils/arm/jni_macro_assembler_arm_vixl.h"
 
 #include "base/hex_dump.h"
+#include "base/malloc_arena_pool.h"
 #include "common_runtime_test.h"
 
 namespace art {
@@ -169,7 +170,7 @@
  public:
   ArmVIXLAssemblerTest() : pool(), allocator(&pool), assembler(&allocator) { }
 
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator;
   ArmVIXLJNIMacroAssembler assembler;
 };
diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h
index 4bd323d..9915498 100644
--- a/compiler/utils/atomic_dex_ref_map-inl.h
+++ b/compiler/utils/atomic_dex_ref_map-inl.h
@@ -81,8 +81,7 @@
   if (array == nullptr) {
     return false;
   }
-  *out = (*array)[ref.index].load(std::memory_order_relaxed);
-  (*array)[ref.index].store(nullptr, std::memory_order_seq_cst);
+  *out = (*array)[ref.index].exchange(nullptr, std::memory_order_seq_cst);
   return true;
 }
 
diff --git a/compiler/utils/jni_macro_assembler.cc b/compiler/utils/jni_macro_assembler.cc
index 3f7691b..0c34aa4f 100644
--- a/compiler/utils/jni_macro_assembler.cc
+++ b/compiler/utils/jni_macro_assembler.cc
@@ -38,8 +38,8 @@
 #include "x86_64/jni_macro_assembler_x86_64.h"
 #endif
 #include "base/casts.h"
+#include "base/memory_region.h"
 #include "globals.h"
-#include "memory_region.h"
 
 namespace art {
 
diff --git a/compiler/utils/jni_macro_assembler_test.h b/compiler/utils/jni_macro_assembler_test.h
index 1aefc84..b70c18b 100644
--- a/compiler/utils/jni_macro_assembler_test.h
+++ b/compiler/utils/jni_macro_assembler_test.h
@@ -20,6 +20,7 @@
 #include "jni_macro_assembler.h"
 
 #include "assembler_test_base.h"
+#include "base/malloc_arena_pool.h"
 #include "common_runtime_test.h"  // For ScratchFile
 
 #include <sys/stat.h>
@@ -139,7 +140,7 @@
     test_helper_->Driver(*data, assembly_text, test_name);
   }
 
-  ArenaPool pool_;
+  MallocArenaPool pool_;
   std::unique_ptr<ArenaAllocator> allocator_;
   std::unique_ptr<Ass> assembler_;
   std::unique_ptr<AssemblerTestInfrastructure> test_helper_;
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index b2ad490..dce5b95 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -18,9 +18,9 @@
 
 #include "base/bit_utils.h"
 #include "base/casts.h"
+#include "base/memory_region.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
-#include "memory_region.h"
 #include "thread.h"
 
 namespace art {
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 5a817fa..bb1bb82 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -18,9 +18,9 @@
 
 #include "base/bit_utils.h"
 #include "base/casts.h"
+#include "base/memory_region.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
-#include "memory_region.h"
 #include "thread.h"
 
 namespace art {
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 42c2541..86f9010 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -17,8 +17,8 @@
 #include "assembler_x86.h"
 
 #include "base/casts.h"
+#include "base/memory_region.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "memory_region.h"
 #include "thread.h"
 
 namespace art {
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 8f72db7..cd007b3 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -17,13 +17,14 @@
 #include "assembler_x86.h"
 
 #include "base/arena_allocator.h"
+#include "base/malloc_arena_pool.h"
 #include "base/stl_util.h"
 #include "utils/assembler_test.h"
 
 namespace art {
 
 TEST(AssemblerX86, CreateBuffer) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
   AssemblerBuffer buffer(&allocator);
   AssemblerBuffer::EnsureCapacity ensured(&buffer);
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index c6e16e7..bd31561 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -17,8 +17,8 @@
 #include "assembler_x86_64.h"
 
 #include "base/casts.h"
+#include "base/memory_region.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "memory_region.h"
 #include "thread.h"
 
 namespace art {
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 104e215..0589df5 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -21,6 +21,7 @@
 #include <random>
 
 #include "base/bit_utils.h"
+#include "base/malloc_arena_pool.h"
 #include "base/stl_util.h"
 #include "jni_macro_assembler_x86_64.h"
 #include "utils/assembler_test.h"
@@ -29,7 +30,7 @@
 namespace art {
 
 TEST(AssemblerX86_64, CreateBuffer) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
   AssemblerBuffer buffer(&allocator);
   AssemblerBuffer::EnsureCapacity ensured(&buffer);
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
index 5766f9d..9486cb4 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
@@ -17,8 +17,8 @@
 #include "jni_macro_assembler_x86_64.h"
 
 #include "base/casts.h"
+#include "base/memory_region.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "memory_region.h"
 #include "thread.h"
 
 namespace art {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6950b93..e217769 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -404,6 +404,7 @@
   UsageError("      Example: --very-large-app-threshold=100000000");
   UsageError("");
   UsageError("  --app-image-fd=<file-descriptor>: specify output file descriptor for app image.");
+  UsageError("      The image is non-empty only if a profile is passed in.");
   UsageError("      Example: --app-image-fd=10");
   UsageError("");
   UsageError("  --app-image-file=<file-name>: specify a file name for app image.");
@@ -453,7 +454,9 @@
   UsageError("      The image writer will group them together.");
   UsageError("");
   UsageError("  --compact-dex-level=none|fast: None avoids generating compact dex, fast");
-  UsageError("      generates compact dex with low compile time.");
+  UsageError("      generates compact dex with low compile time. If speed-profile is specified as");
+  UsageError("      the compiler filter and the profile is not empty, the default compact dex");
+  UsageError("      level is always used.");
   UsageError("");
   UsageError("  --deduplicate-code=true|false: enable|disable code deduplication. Deduplicated");
   UsageError("      code will have an arbitrary symbol tagged with [DEDUPED].");
@@ -1479,9 +1482,15 @@
   }
 
   void LoadClassProfileDescriptors() {
-    if (profile_compilation_info_ != nullptr && IsImage()) {
-      Runtime* runtime = Runtime::Current();
-      CHECK(runtime != nullptr);
+    if (!IsImage()) {
+      return;
+    }
+    // If we don't have a profile, treat it as an empty set of classes. b/77340429
+    if (image_classes_ == nullptr) {
+      // May be non-null when --image-classes is passed in, in that case avoid clearing the list.
+      image_classes_.reset(new std::unordered_set<std::string>());
+    }
+    if (profile_compilation_info_ != nullptr) {
       // Filter out class path classes since we don't want to include these in the image.
       image_classes_.reset(
           new std::unordered_set<std::string>(
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 0cd39ac..c890f8be 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -2093,4 +2093,36 @@
   ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_;
 }
 
+TEST_F(Dex2oatTest, AppImageNoProfile) {
+  ScratchFile app_image_file;
+  const std::string out_dir = GetScratchDir();
+  const std::string odex_location = out_dir + "/base.odex";
+  GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
+                      odex_location,
+                      CompilerFilter::Filter::kSpeedProfile,
+                      { "--app-image-fd=" + std::to_string(app_image_file.GetFd()) },
+                      true,  // expect_success
+                      false,  // use_fd
+                      [](const OatFile&) {});
+  // Open our generated oat file.
+  std::string error_msg;
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                   odex_location.c_str(),
+                                                   nullptr,
+                                                   nullptr,
+                                                   false,
+                                                   /*low_4gb*/false,
+                                                   odex_location.c_str(),
+                                                   &error_msg));
+  ASSERT_TRUE(odex_file != nullptr);
+  ImageHeader header = {};
+  ASSERT_TRUE(app_image_file.GetFile()->PreadFully(
+      reinterpret_cast<void*>(&header),
+      sizeof(header),
+      /*offset*/ 0u)) << app_image_file.GetFile()->GetLength();
+  EXPECT_GT(header.GetImageSection(ImageHeader::kSectionObjects).Size(), 0u);
+  EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtMethods).Size(), 0u);
+  EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtFields).Size(), 0u);
+}
+
 }  // namespace art
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 65a4c5b..bcc5909 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -445,6 +445,11 @@
     absolute_patch_locations_(),
     profile_compilation_info_(info),
     compact_dex_level_(compact_dex_level) {
+  // If we have a profile, always use at least the default compact dex level. The reason behind
+  // this is that CompactDex conversion is not more expensive than normal dexlayout.
+  if (info != nullptr && compact_dex_level_ == CompactDexLevel::kCompactDexLevelNone) {
+    compact_dex_level_ = kDefaultCompactDexLevel;
+  }
 }
 
 static bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location) {
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 3c61944..62157e1 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -20,13 +20,18 @@
     host_supported: true,
     srcs: [
         "base/allocator.cc",
+        "base/arena_allocator.cc",
+        "base/arena_bit_vector.cc",
         "base/bit_vector.cc",
         "base/file_magic.cc",
         "base/hex_dump.cc",
         "base/logging.cc",
+        "base/malloc_arena_pool.cc",
+        "base/memory_region.cc",
         "base/os_linux.cc",
         "base/runtime_debug.cc",
         "base/safe_copy.cc",
+        "base/scoped_arena_allocator.cc",
         "base/scoped_flock.cc",
         "base/time_utils.cc",
         "base/unix_file/fd_file.cc",
@@ -90,6 +95,7 @@
         "art_gtest_defaults",
     ],
     srcs: [
+        "base/arena_allocator_test.cc",
         "base/bit_field_test.cc",
         "base/bit_string_test.cc",
         "base/bit_struct_test.cc",
@@ -100,6 +106,7 @@
         "base/histogram_test.cc",
         "base/leb128_test.cc",
         "base/logging_test.cc",
+        "base/memory_region_test.cc",
         "base/safe_copy_test.cc",
         "base/scoped_flock_test.cc",
         "base/time_utils_test.cc",
diff --git a/libartbase/base/allocator.cc b/libartbase/base/allocator.cc
index 17da789..c7be4e0 100644
--- a/libartbase/base/allocator.cc
+++ b/libartbase/base/allocator.cc
@@ -76,7 +76,7 @@
 
 // These globals are safe since they don't have any non-trivial destructors.
 Atomic<size_t> g_bytes_used[kAllocatorTagCount];
-volatile size_t g_max_bytes_used[kAllocatorTagCount];
+Atomic<size_t> g_max_bytes_used[kAllocatorTagCount];
 Atomic<uint64_t> g_total_bytes_used[kAllocatorTagCount];
 
 void Dump(std::ostream& os) {
@@ -84,7 +84,7 @@
     os << "Dumping native memory usage\n";
     for (size_t i = 0; i < kAllocatorTagCount; ++i) {
       uint64_t bytes_used = g_bytes_used[i].load(std::memory_order_relaxed);
-      uint64_t max_bytes_used = g_max_bytes_used[i];
+      uint64_t max_bytes_used = g_max_bytes_used[i].load(std::memory_order_relaxed);
       uint64_t total_bytes_used = g_total_bytes_used[i].load(std::memory_order_relaxed);
       if (total_bytes_used != 0) {
         os << static_cast<AllocatorTag>(i) << " active=" << bytes_used << " max="
diff --git a/libartbase/base/allocator.h b/libartbase/base/allocator.h
index 7ddbacf..662f78e 100644
--- a/libartbase/base/allocator.h
+++ b/libartbase/base/allocator.h
@@ -71,12 +71,14 @@
 
 namespace TrackedAllocators {
 
+// We use memory_order_relaxed updates of the following counters. Values are treated as approximate
+// wherever concurrent updates are possible.
 // Running count of number of bytes used for this kind of allocation. Increased by allocations,
 // decreased by deallocations.
 extern Atomic<size_t> g_bytes_used[kAllocatorTagCount];
 
 // Largest value of bytes used seen.
-extern volatile size_t g_max_bytes_used[kAllocatorTagCount];
+extern Atomic<size_t> g_max_bytes_used[kAllocatorTagCount];
 
 // Total number of bytes allocated of this kind.
 extern Atomic<uint64_t> g_total_bytes_used[kAllocatorTagCount];
@@ -84,15 +86,17 @@
 void Dump(std::ostream& os);
 
 inline void RegisterAllocation(AllocatorTag tag, size_t bytes) {
-  g_total_bytes_used[tag].fetch_add(bytes, std::memory_order_seq_cst);
-  size_t new_bytes = g_bytes_used[tag].fetch_add(bytes, std::memory_order_seq_cst) + bytes;
-  if (g_max_bytes_used[tag] < new_bytes) {
-    g_max_bytes_used[tag] = new_bytes;
+  g_total_bytes_used[tag].fetch_add(bytes, std::memory_order_relaxed);
+  size_t new_bytes = g_bytes_used[tag].fetch_add(bytes, std::memory_order_relaxed) + bytes;
+  size_t max_bytes = g_max_bytes_used[tag].load(std::memory_order_relaxed);
+  while (max_bytes < new_bytes
+    && !g_max_bytes_used[tag].compare_exchange_weak(max_bytes /* updated */, new_bytes,
+                                                    std::memory_order_relaxed)) {
   }
 }
 
 inline void RegisterFree(AllocatorTag tag, size_t bytes) {
-  g_bytes_used[tag].fetch_sub(bytes, std::memory_order_seq_cst);
+  g_bytes_used[tag].fetch_sub(bytes, std::memory_order_relaxed);
 }
 
 }  // namespace TrackedAllocators
diff --git a/runtime/base/arena_allocator-inl.h b/libartbase/base/arena_allocator-inl.h
similarity index 86%
rename from runtime/base/arena_allocator-inl.h
rename to libartbase/base/arena_allocator-inl.h
index 0e43837..a03e9df 100644
--- a/runtime/base/arena_allocator-inl.h
+++ b/libartbase/base/arena_allocator-inl.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_
-#define ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_
+#ifndef ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_
+#define ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_
 
 #include "arena_allocator.h"
 
@@ -31,4 +31,4 @@
 }  // namespace arena_allocator
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_
+#endif  // ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_
diff --git a/runtime/base/arena_allocator.cc b/libartbase/base/arena_allocator.cc
similarity index 67%
rename from runtime/base/arena_allocator.cc
rename to libartbase/base/arena_allocator.cc
index fe0f876..348a812 100644
--- a/runtime/base/arena_allocator.cc
+++ b/libartbase/base/arena_allocator.cc
@@ -25,11 +25,6 @@
 
 #include <android-base/logging.h>
 
-#include "base/systrace.h"
-#include "mem_map.h"
-#include "mutex.h"
-#include "thread-current-inl.h"
-
 namespace art {
 
 constexpr size_t kMemoryToolRedZoneBytes = 8;
@@ -190,194 +185,6 @@
 Arena::Arena() : bytes_allocated_(0), memory_(nullptr), size_(0), next_(nullptr) {
 }
 
-class MallocArena FINAL : public Arena {
- public:
-  explicit MallocArena(size_t size = arena_allocator::kArenaDefaultSize);
-  virtual ~MallocArena();
- private:
-  static constexpr size_t RequiredOverallocation() {
-    return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment)
-        ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t)
-        : 0u;
-  }
-
-  uint8_t* unaligned_memory_;
-};
-
-MallocArena::MallocArena(size_t size) {
-  // We need to guarantee kArenaAlignment aligned allocation for the new arena.
-  // TODO: Use std::aligned_alloc() when it becomes available with C++17.
-  constexpr size_t overallocation = RequiredOverallocation();
-  unaligned_memory_ = reinterpret_cast<uint8_t*>(calloc(1, size + overallocation));
-  CHECK(unaligned_memory_ != nullptr);  // Abort on OOM.
-  DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t));
-  if (overallocation == 0u) {
-    memory_ = unaligned_memory_;
-  } else {
-    memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment);
-    if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
-      size_t head = memory_ - unaligned_memory_;
-      size_t tail = overallocation - head;
-      MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head);
-      MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail);
-    }
-  }
-  DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
-  size_ = size;
-}
-
-MallocArena::~MallocArena() {
-  constexpr size_t overallocation = RequiredOverallocation();
-  if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
-    size_t head = memory_ - unaligned_memory_;
-    size_t tail = overallocation - head;
-    MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head);
-    MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail);
-  }
-  free(reinterpret_cast<void*>(unaligned_memory_));
-}
-
-class MemMapArena FINAL : public Arena {
- public:
-  MemMapArena(size_t size, bool low_4gb, const char* name);
-  virtual ~MemMapArena();
-  void Release() OVERRIDE;
-
- private:
-  std::unique_ptr<MemMap> map_;
-};
-
-MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) {
-  // Round up to a full page as that's the smallest unit of allocation for mmap()
-  // and we want to be able to use all memory that we actually allocate.
-  size = RoundUp(size, kPageSize);
-  std::string error_msg;
-  map_.reset(MemMap::MapAnonymous(
-      name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg));
-  CHECK(map_.get() != nullptr) << error_msg;
-  memory_ = map_->Begin();
-  static_assert(ArenaAllocator::kArenaAlignment <= kPageSize,
-                "Arena should not need stronger alignment than kPageSize.");
-  DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
-  size_ = map_->Size();
-}
-
-MemMapArena::~MemMapArena() {
-  // Destroys MemMap via std::unique_ptr<>.
-}
-
-void MemMapArena::Release() {
-  if (bytes_allocated_ > 0) {
-    map_->MadviseDontNeedAndZero();
-    bytes_allocated_ = 0;
-  }
-}
-
-void Arena::Reset() {
-  if (bytes_allocated_ > 0) {
-    memset(Begin(), 0, bytes_allocated_);
-    bytes_allocated_ = 0;
-  }
-}
-
-ArenaPool::ArenaPool(bool use_malloc, bool low_4gb, const char* name)
-    : use_malloc_(use_malloc),
-      lock_("Arena pool lock", kArenaPoolLock),
-      free_arenas_(nullptr),
-      low_4gb_(low_4gb),
-      name_(name) {
-  if (low_4gb) {
-    CHECK(!use_malloc) << "low4gb must use map implementation";
-  }
-  if (!use_malloc) {
-    MemMap::Init();
-  }
-}
-
-ArenaPool::~ArenaPool() {
-  ReclaimMemory();
-}
-
-void ArenaPool::ReclaimMemory() {
-  while (free_arenas_ != nullptr) {
-    Arena* arena = free_arenas_;
-    free_arenas_ = free_arenas_->next_;
-    delete arena;
-  }
-}
-
-void ArenaPool::LockReclaimMemory() {
-  MutexLock lock(Thread::Current(), lock_);
-  ReclaimMemory();
-}
-
-Arena* ArenaPool::AllocArena(size_t size) {
-  Thread* self = Thread::Current();
-  Arena* ret = nullptr;
-  {
-    MutexLock lock(self, lock_);
-    if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
-      ret = free_arenas_;
-      free_arenas_ = free_arenas_->next_;
-    }
-  }
-  if (ret == nullptr) {
-    ret = use_malloc_ ? static_cast<Arena*>(new MallocArena(size)) :
-        new MemMapArena(size, low_4gb_, name_);
-  }
-  ret->Reset();
-  return ret;
-}
-
-void ArenaPool::TrimMaps() {
-  if (!use_malloc_) {
-    ScopedTrace trace(__PRETTY_FUNCTION__);
-    // Doesn't work for malloc.
-    MutexLock lock(Thread::Current(), lock_);
-    for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
-      arena->Release();
-    }
-  }
-}
-
-size_t ArenaPool::GetBytesAllocated() const {
-  size_t total = 0;
-  MutexLock lock(Thread::Current(), lock_);
-  for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
-    total += arena->GetBytesAllocated();
-  }
-  return total;
-}
-
-void ArenaPool::FreeArenaChain(Arena* first) {
-  if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
-    for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
-      MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
-    }
-  }
-
-  if (arena_allocator::kArenaAllocatorPreciseTracking) {
-    // Do not reuse arenas when tracking.
-    while (first != nullptr) {
-      Arena* next = first->next_;
-      delete first;
-      first = next;
-    }
-    return;
-  }
-
-  if (first != nullptr) {
-    Arena* last = first;
-    while (last->next_ != nullptr) {
-      last = last->next_;
-    }
-    Thread* self = Thread::Current();
-    MutexLock lock(self, lock_);
-    last->next_ = free_arenas_;
-    free_arenas_ = first;
-  }
-}
-
 size_t ArenaAllocator::BytesAllocated() const {
   return ArenaAllocatorStats::BytesAllocated();
 }
diff --git a/runtime/base/arena_allocator.h b/libartbase/base/arena_allocator.h
similarity index 93%
rename from runtime/base/arena_allocator.h
rename to libartbase/base/arena_allocator.h
index 688f01b..3143fba 100644
--- a/runtime/base/arena_allocator.h
+++ b/libartbase/base/arena_allocator.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_
-#define ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_
+#ifndef ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_
+#define ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_
 
 #include <stddef.h>
 #include <stdint.h>
@@ -25,7 +25,6 @@
 #include "base/dchecked_vector.h"
 #include "base/macros.h"
 #include "base/memory_tool.h"
-#include "mutex.h"
 
 namespace art {
 
@@ -236,7 +235,8 @@
   uint8_t* memory_;
   size_t size_;
   Arena* next_;
-  friend class ArenaPool;
+  friend class MallocArenaPool;
+  friend class MemMapArenaPool;
   friend class ArenaAllocator;
   friend class ArenaStack;
   friend class ScopedArenaAllocator;
@@ -250,25 +250,20 @@
 
 class ArenaPool {
  public:
-  explicit ArenaPool(bool use_malloc = true,
-                     bool low_4gb = false,
-                     const char* name = "LinearAlloc");
-  ~ArenaPool();
-  Arena* AllocArena(size_t size) REQUIRES(!lock_);
-  void FreeArenaChain(Arena* first) REQUIRES(!lock_);
-  size_t GetBytesAllocated() const REQUIRES(!lock_);
-  void ReclaimMemory() NO_THREAD_SAFETY_ANALYSIS;
-  void LockReclaimMemory() REQUIRES(!lock_);
-  // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. This only works
-  // use_malloc is false.
-  void TrimMaps() REQUIRES(!lock_);
+  virtual ~ArenaPool() = default;
+
+  virtual Arena* AllocArena(size_t size) = 0;
+  virtual void FreeArenaChain(Arena* first) = 0;
+  virtual size_t GetBytesAllocated() const = 0;
+  virtual void ReclaimMemory() = 0;
+  virtual void LockReclaimMemory() = 0;
+  // Trim the maps in arenas by madvising, used by JIT to reduce memory usage.
+  virtual void TrimMaps() = 0;
+
+ protected:
+  ArenaPool() = default;
 
  private:
-  const bool use_malloc_;
-  mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  Arena* free_arenas_ GUARDED_BY(lock_);
-  const bool low_4gb_;
-  const char* name_;
   DISALLOW_COPY_AND_ASSIGN(ArenaPool);
 };
 
@@ -428,4 +423,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_
+#endif  // ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_
diff --git a/runtime/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc
similarity index 95%
rename from runtime/base/arena_allocator_test.cc
rename to libartbase/base/arena_allocator_test.cc
index 68e26af..68e99f4 100644
--- a/runtime/base/arena_allocator_test.cc
+++ b/libartbase/base/arena_allocator_test.cc
@@ -16,6 +16,7 @@
 
 #include "base/arena_allocator-inl.h"
 #include "base/arena_bit_vector.h"
+#include "base/malloc_arena_pool.h"
 #include "base/memory_tool.h"
 #include "gtest/gtest.h"
 
@@ -33,7 +34,7 @@
 };
 
 TEST_F(ArenaAllocatorTest, Test) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
   ArenaBitVector bv(&allocator, 10, true);
   bv.SetBit(5);
@@ -44,7 +45,7 @@
 
 TEST_F(ArenaAllocatorTest, MakeDefined) {
   // Regression test to make sure we mark the allocated area defined.
-  ArenaPool pool;
+  MallocArenaPool pool;
   static constexpr size_t kSmallArraySize = 10;
   static constexpr size_t kLargeArraySize = 50;
   uint32_t* small_array;
@@ -71,7 +72,7 @@
   }
 
   {
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
     // Note: Leaving some space for memory tool red zones.
     void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 5 / 8);
@@ -80,7 +81,7 @@
     ASSERT_EQ(1u, NumberOfArenas(&allocator));
   }
   {
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
     void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16);
     void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 11 / 16);
@@ -92,7 +93,7 @@
     ASSERT_EQ(3u, NumberOfArenas(&allocator));
   }
   {
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
     void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16);
     void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 9 / 16);
@@ -105,7 +106,7 @@
     ASSERT_EQ(2u, NumberOfArenas(&allocator));
   }
   {
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
     void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 9 / 16);
     void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16);
@@ -118,7 +119,7 @@
     ASSERT_EQ(2u, NumberOfArenas(&allocator));
   }
   {
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
     // Note: Leaving some space for memory tool red zones.
     for (size_t i = 0; i != 15; ++i) {
@@ -133,7 +134,7 @@
 }
 
 TEST_F(ArenaAllocatorTest, AllocAlignment) {
-  ArenaPool pool;
+  MallocArenaPool pool;
   ArenaAllocator allocator(&pool);
   for (size_t iterations = 0; iterations <= 10; ++iterations) {
     for (size_t size = 1; size <= ArenaAllocator::kAlignment + 1; ++size) {
@@ -153,7 +154,7 @@
 
   {
     // Case 1: small aligned allocation, aligned extend inside arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = ArenaAllocator::kAlignment * 2;
@@ -166,7 +167,7 @@
 
   {
     // Case 2: small aligned allocation, non-aligned extend inside arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = ArenaAllocator::kAlignment * 2;
@@ -179,7 +180,7 @@
 
   {
     // Case 3: small non-aligned allocation, aligned extend inside arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2);
@@ -192,7 +193,7 @@
 
   {
     // Case 4: small non-aligned allocation, aligned non-extend inside arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2);
@@ -208,7 +209,7 @@
 
   {
     // Case 5: large allocation, aligned extend into next arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = arena_allocator::kArenaDefaultSize -
@@ -222,7 +223,7 @@
 
   {
     // Case 6: large allocation, non-aligned extend into next arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = arena_allocator::kArenaDefaultSize -
@@ -241,7 +242,7 @@
 TEST_F(ArenaAllocatorTest, ReallocAlignment) {
   {
     // Case 1: small aligned allocation, aligned extend inside arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = ArenaAllocator::kAlignment * 2;
@@ -258,7 +259,7 @@
 
   {
     // Case 2: small aligned allocation, non-aligned extend inside arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = ArenaAllocator::kAlignment * 2;
@@ -275,7 +276,7 @@
 
   {
     // Case 3: small non-aligned allocation, aligned extend inside arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2);
@@ -292,7 +293,7 @@
 
   {
     // Case 4: small non-aligned allocation, aligned non-extend inside arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2);
@@ -312,7 +313,7 @@
 
   {
     // Case 5: large allocation, aligned extend into next arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = arena_allocator::kArenaDefaultSize -
@@ -330,7 +331,7 @@
 
   {
     // Case 6: large allocation, non-aligned extend into next arena.
-    ArenaPool pool;
+    MallocArenaPool pool;
     ArenaAllocator allocator(&pool);
 
     const size_t original_size = arena_allocator::kArenaDefaultSize -
diff --git a/runtime/base/arena_bit_vector.cc b/libartbase/base/arena_bit_vector.cc
similarity index 100%
rename from runtime/base/arena_bit_vector.cc
rename to libartbase/base/arena_bit_vector.cc
diff --git a/runtime/base/arena_bit_vector.h b/libartbase/base/arena_bit_vector.h
similarity index 92%
rename from runtime/base/arena_bit_vector.h
rename to libartbase/base/arena_bit_vector.h
index ca1d5b1..2b2322e 100644
--- a/runtime/base/arena_bit_vector.h
+++ b/libartbase/base/arena_bit_vector.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_
-#define ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_
+#ifndef ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_
+#define ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_
 
 #include "base/arena_object.h"
 #include "base/bit_vector.h"
@@ -55,4 +55,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_
+#endif  // ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_
diff --git a/runtime/base/arena_containers.h b/libartbase/base/arena_containers.h
similarity index 97%
rename from runtime/base/arena_containers.h
rename to libartbase/base/arena_containers.h
index 4f572120..40cf23c 100644
--- a/runtime/base/arena_containers.h
+++ b/libartbase/base/arena_containers.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_ARENA_CONTAINERS_H_
-#define ART_RUNTIME_BASE_ARENA_CONTAINERS_H_
+#ifndef ART_LIBARTBASE_BASE_ARENA_CONTAINERS_H_
+#define ART_LIBARTBASE_BASE_ARENA_CONTAINERS_H_
 
 #include <deque>
 #include <queue>
@@ -241,4 +241,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_ARENA_CONTAINERS_H_
+#endif  // ART_LIBARTBASE_BASE_ARENA_CONTAINERS_H_
diff --git a/runtime/base/arena_object.h b/libartbase/base/arena_object.h
similarity index 93%
rename from runtime/base/arena_object.h
rename to libartbase/base/arena_object.h
index d01e346..de7cb64 100644
--- a/runtime/base/arena_object.h
+++ b/libartbase/base/arena_object.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_ARENA_OBJECT_H_
-#define ART_RUNTIME_BASE_ARENA_OBJECT_H_
+#ifndef ART_LIBARTBASE_BASE_ARENA_OBJECT_H_
+#define ART_LIBARTBASE_BASE_ARENA_OBJECT_H_
 
 #include <android-base/logging.h>
 
@@ -69,4 +69,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_ARENA_OBJECT_H_
+#endif  // ART_LIBARTBASE_BASE_ARENA_OBJECT_H_
diff --git a/runtime/bit_memory_region.h b/libartbase/base/bit_memory_region.h
similarity index 93%
rename from runtime/bit_memory_region.h
rename to libartbase/base/bit_memory_region.h
index 3a696f1..f3926bc 100644
--- a/runtime/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BIT_MEMORY_REGION_H_
-#define ART_RUNTIME_BIT_MEMORY_REGION_H_
+#ifndef ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_
+#define ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_
 
 #include "memory_region.h"
 
@@ -70,4 +70,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BIT_MEMORY_REGION_H_
+#endif  // ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_
diff --git a/runtime/base/dumpable.h b/libartbase/base/dumpable.h
similarity index 63%
rename from runtime/base/dumpable.h
rename to libartbase/base/dumpable.h
index 9ef8d69..6621397 100644
--- a/runtime/base/dumpable.h
+++ b/libartbase/base/dumpable.h
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_DUMPABLE_H_
-#define ART_RUNTIME_BASE_DUMPABLE_H_
+#ifndef ART_LIBARTBASE_BASE_DUMPABLE_H_
+#define ART_LIBARTBASE_BASE_DUMPABLE_H_
 
 #include <ostream>
 
 #include "base/macros.h"
-#include "base/mutex.h"
 
 namespace art {
 
@@ -51,27 +50,6 @@
   return os;
 }
 
-template<typename T>
-class MutatorLockedDumpable {
- public:
-  explicit MutatorLockedDumpable(T& value) REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {}
-
-  void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) {
-    value_.Dump(os);
-  }
-
- private:
-  const T& value_;
-
-  DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable);
-};
-
-template<typename T>
-std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs)
-  // TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis
-  //       currently fails for this.
-    NO_THREAD_SAFETY_ANALYSIS;
-
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_DUMPABLE_H_
+#endif  // ART_LIBARTBASE_BASE_DUMPABLE_H_
diff --git a/libartbase/base/malloc_arena_pool.cc b/libartbase/base/malloc_arena_pool.cc
new file mode 100644
index 0000000..7df4aef
--- /dev/null
+++ b/libartbase/base/malloc_arena_pool.cc
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "malloc_arena_pool.h"
+
+#include <sys/mman.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <iomanip>
+#include <numeric>
+
+#include <android-base/logging.h>
+#include "base/arena_allocator-inl.h"
+
+namespace art {
+
+class MallocArena FINAL : public Arena {
+ public:
+  explicit MallocArena(size_t size = arena_allocator::kArenaDefaultSize);
+  virtual ~MallocArena();
+ private:
+  static constexpr size_t RequiredOverallocation() {
+    return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment)
+        ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t)
+        : 0u;
+  }
+
+  uint8_t* unaligned_memory_;
+};
+
+MallocArena::MallocArena(size_t size) {
+  // We need to guarantee kArenaAlignment aligned allocation for the new arena.
+  // TODO: Use std::aligned_alloc() when it becomes available with C++17.
+  constexpr size_t overallocation = RequiredOverallocation();
+  unaligned_memory_ = reinterpret_cast<uint8_t*>(calloc(1, size + overallocation));
+  CHECK(unaligned_memory_ != nullptr);  // Abort on OOM.
+  DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t));
+  if (overallocation == 0u) {
+    memory_ = unaligned_memory_;
+  } else {
+    memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment);
+    if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+      size_t head = memory_ - unaligned_memory_;
+      size_t tail = overallocation - head;
+      MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head);
+      MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail);
+    }
+  }
+  DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
+  size_ = size;
+}
+
+MallocArena::~MallocArena() {
+  constexpr size_t overallocation = RequiredOverallocation();
+  if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+    size_t head = memory_ - unaligned_memory_;
+    size_t tail = overallocation - head;
+    MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head);
+    MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail);
+  }
+  free(reinterpret_cast<void*>(unaligned_memory_));
+}
+
+void Arena::Reset() {
+  if (bytes_allocated_ > 0) {
+    memset(Begin(), 0, bytes_allocated_);
+    bytes_allocated_ = 0;
+  }
+}
+
+MallocArenaPool::MallocArenaPool() : free_arenas_(nullptr) {
+}
+
+MallocArenaPool::~MallocArenaPool() {
+  ReclaimMemory();
+}
+
+void MallocArenaPool::ReclaimMemory() {
+  while (free_arenas_ != nullptr) {
+    Arena* arena = free_arenas_;
+    free_arenas_ = free_arenas_->next_;
+    delete arena;
+  }
+}
+
+void MallocArenaPool::LockReclaimMemory() {
+  std::lock_guard<std::mutex> lock(lock_);
+  ReclaimMemory();
+}
+
+Arena* MallocArenaPool::AllocArena(size_t size) {
+  Arena* ret = nullptr;
+  {
+    std::lock_guard<std::mutex> lock(lock_);
+    if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
+      ret = free_arenas_;
+      free_arenas_ = free_arenas_->next_;
+    }
+  }
+  if (ret == nullptr) {
+    ret = new MallocArena(size);
+  }
+  ret->Reset();
+  return ret;
+}
+
+void MallocArenaPool::TrimMaps() {
+  // Nop, because there is no way to do madvise here.
+}
+
+size_t MallocArenaPool::GetBytesAllocated() const {
+  size_t total = 0;
+  std::lock_guard<std::mutex> lock(lock_);
+  for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
+    total += arena->GetBytesAllocated();
+  }
+  return total;
+}
+
+void MallocArenaPool::FreeArenaChain(Arena* first) {
+  if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+    for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
+      MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
+    }
+  }
+
+  if (arena_allocator::kArenaAllocatorPreciseTracking) {
+    // Do not reuse arenas when tracking.
+    while (first != nullptr) {
+      Arena* next = first->next_;
+      delete first;
+      first = next;
+    }
+    return;
+  }
+
+  if (first != nullptr) {
+    Arena* last = first;
+    while (last->next_ != nullptr) {
+      last = last->next_;
+    }
+    std::lock_guard<std::mutex> lock(lock_);
+    last->next_ = free_arenas_;
+    free_arenas_ = first;
+  }
+}
+
+}  // namespace art
diff --git a/libartbase/base/malloc_arena_pool.h b/libartbase/base/malloc_arena_pool.h
new file mode 100644
index 0000000..8720189
--- /dev/null
+++ b/libartbase/base/malloc_arena_pool.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_
+#define ART_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_
+
+#include <mutex>
+
+#include "base/arena_allocator.h"
+
+namespace art {
+
+class MallocArenaPool FINAL : public ArenaPool {
+ public:
+  MallocArenaPool();
+  ~MallocArenaPool();
+  Arena* AllocArena(size_t size) OVERRIDE;
+  void FreeArenaChain(Arena* first) OVERRIDE;
+  size_t GetBytesAllocated() const OVERRIDE;
+  void ReclaimMemory() OVERRIDE;
+  void LockReclaimMemory() OVERRIDE;
+  // Is a nop for malloc pools.
+  void TrimMaps() OVERRIDE;
+
+ private:
+  Arena* free_arenas_;
+  // Use a std::mutex here as Arenas are at the bottom of the lock hierarchy when malloc is used.
+  mutable std::mutex lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(MallocArenaPool);
+};
+
+}  // namespace art
+
+#endif  // ART_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_
diff --git a/runtime/memory_region.cc b/libartbase/base/memory_region.cc
similarity index 100%
rename from runtime/memory_region.cc
rename to libartbase/base/memory_region.cc
diff --git a/runtime/memory_region.h b/libartbase/base/memory_region.h
similarity index 95%
rename from runtime/memory_region.h
rename to libartbase/base/memory_region.h
index 23e0aec..7add466 100644
--- a/runtime/memory_region.h
+++ b/libartbase/base/memory_region.h
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_MEMORY_REGION_H_
-#define ART_RUNTIME_MEMORY_REGION_H_
+#ifndef ART_LIBARTBASE_BASE_MEMORY_REGION_H_
+#define ART_LIBARTBASE_BASE_MEMORY_REGION_H_
 
 #include <stdint.h>
 #include <type_traits>
 
 #include <android-base/logging.h>
 
-#include "arch/instruction_set.h"
 #include "base/bit_utils.h"
 #include "base/casts.h"
+#include "base/enums.h"
 #include "base/macros.h"
 #include "base/value_object.h"
 #include "globals.h"
@@ -211,9 +211,8 @@
 
   // Is `address` aligned on a machine word?
   template<typename T> static constexpr bool IsWordAligned(const T* address) {
-    // Word alignment in bytes.
-    size_t kWordAlignment = static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA));
-    return IsAlignedParam(address, kWordAlignment);
+    // Word alignment in bytes.  Determined from pointer size.
+    return IsAligned<kRuntimePointerSize>(address);
   }
 
   void* pointer_;
@@ -222,4 +221,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_MEMORY_REGION_H_
+#endif  // ART_LIBARTBASE_BASE_MEMORY_REGION_H_
diff --git a/runtime/memory_region_test.cc b/libartbase/base/memory_region_test.cc
similarity index 100%
rename from runtime/memory_region_test.cc
rename to libartbase/base/memory_region_test.cc
diff --git a/runtime/base/scoped_arena_allocator.cc b/libartbase/base/scoped_arena_allocator.cc
similarity index 100%
rename from runtime/base/scoped_arena_allocator.cc
rename to libartbase/base/scoped_arena_allocator.cc
diff --git a/runtime/base/scoped_arena_allocator.h b/libartbase/base/scoped_arena_allocator.h
similarity index 96%
rename from runtime/base/scoped_arena_allocator.h
rename to libartbase/base/scoped_arena_allocator.h
index a253e2f..d5f6df8 100644
--- a/runtime/base/scoped_arena_allocator.h
+++ b/libartbase/base/scoped_arena_allocator.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_
-#define ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_
+#ifndef ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_
+#define ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_
 
 #include <android-base/logging.h>
 
@@ -185,4 +185,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_
+#endif  // ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_
diff --git a/runtime/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h
similarity index 97%
rename from runtime/base/scoped_arena_containers.h
rename to libartbase/base/scoped_arena_containers.h
index f8ee3f3..4df02b6 100644
--- a/runtime/base/scoped_arena_containers.h
+++ b/libartbase/base/scoped_arena_containers.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_
-#define ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_
+#ifndef ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_
+#define ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_
 
 #include <deque>
 #include <queue>
@@ -272,4 +272,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_
+#endif  // ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h
index 441b3c1..b62d044 100644
--- a/libdexfile/dex/hidden_api_access_flags.h
+++ b/libdexfile/dex/hidden_api_access_flags.h
@@ -18,6 +18,7 @@
 #define ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_
 
 #include "base/bit_utils.h"
+#include "base/macros.h"
 #include "dex/modifiers.h"
 
 namespace art {
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 433ed9a..3bff123 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1180,6 +1180,17 @@
       }
     }
 
+    // Update header for shared section.
+    uint32_t shared_section_offset = 0u;
+    uint32_t shared_section_size = 0u;
+    if (dex_file->IsCompactDexFile()) {
+      CompactDexFile::Header* const header =
+          reinterpret_cast<CompactDexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()));
+      shared_section_offset = header->data_off_;
+      shared_section_size = header->data_size_;
+      // The shared section will be serialized right after the dex file.
+      header->data_off_ = header->file_size_;
+    }
     // Verify output directory exists
     if (!OS::DirectoryExists(options_.export_dex_location_)) {
       // TODO: Extend OS::DirectoryExists if symlink support is required
@@ -1226,16 +1237,22 @@
       return false;
     }
 
-    bool success = false;
-      success = file->WriteFully(dex_file->Begin(), fsize);
-    // }
-
+    bool success = file->WriteFully(dex_file->Begin(), fsize);
     if (!success) {
       os << "Failed to write dex file";
       file->Erase();
       return false;
     }
 
+    if (shared_section_size != 0) {
+      success = file->WriteFully(dex_file->Begin() + shared_section_offset, shared_section_size);
+      if (!success) {
+        os << "Failed to write shared data section";
+        file->Erase();
+        return false;
+      }
+    }
+
     if (file->FlushCloseOrErase() != 0) {
       os << "Flush and close failed";
       return false;
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index 0034469..b4eddb9 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -72,9 +72,16 @@
 }
 
 TEST_F(OatDumpTest, TestExportDex) {
+  // Test is failing on target, b/77469384.
+  TEST_DISABLED_FOR_TARGET();
   std::string error_msg;
   ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg))
       << error_msg;
+  const std::string dex_location = tmp_dir_+ "/core-oj-hostdex.jar_export.dex";
+  const std::string dexdump2 = GetExecutableFilePath("dexdump2",
+                                                     /*is_debug*/false,
+                                                     /*is_static*/false);
+  ASSERT_TRUE(ForkAndExecAndWait({dexdump2, "-d", dex_location}, &error_msg)) << error_msg;
 }
 TEST_F(OatDumpTest, TestExportDexStatic) {
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index fac0bb2..b85730d 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -70,20 +70,24 @@
     kStatic,   // oatdump(d)s, dex2oat(d)s
   };
 
-  // Returns path to the oatdump/dex2oat binary.
-  std::string GetExecutableFilePath(Flavor flavor, const char* name) {
+  // Returns path to the oatdump/dex2oat/dexdump binary.
+  std::string GetExecutableFilePath(const char* name, bool is_debug, bool is_static) {
     std::string root = GetTestAndroidRoot();
     root += "/bin/";
     root += name;
-    if (kIsDebugBuild) {
+    if (is_debug) {
       root += "d";
     }
-    if (flavor == kStatic) {
+    if (is_static) {
       root += "s";
     }
     return root;
   }
 
+  std::string GetExecutableFilePath(Flavor flavor, const char* name) {
+    return GetExecutableFilePath(name, kIsDebugBuild, flavor == kStatic);
+  }
+
   enum Mode {
     kModeOat,
     kModeOatWithBootImage,
@@ -127,17 +131,7 @@
     };
     exec_argv.insert(exec_argv.end(), args.begin(), args.end());
 
-    pid_t pid;
-    int pipe_fd;
-    bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg);
-    if (result) {
-      close(pipe_fd);
-      int status = 0;
-      if (waitpid(pid, &status, 0) != -1) {
-        result = (status == 0);
-      }
-    }
-    return result;
+    return ForkAndExecAndWait(exec_argv, error_msg);
   }
 
   // Run the test with custom arguments.
@@ -300,6 +294,21 @@
     }
   }
 
+  bool ForkAndExecAndWait(const std::vector<std::string>& exec_argv,
+                          /*out*/ std::string* error_msg) {
+    pid_t pid;
+    int pipe_fd;
+    bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg);
+    if (result) {
+      close(pipe_fd);
+      int status = 0;
+      if (waitpid(pid, &status, 0) != -1) {
+        result = (status == 0);
+      }
+    }
+    return result;
+  }
+
   std::string tmp_dir_;
 
  private:
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index 6d84ffa..a6f1207 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -53,20 +53,29 @@
 namespace openjdkjvmti {
 
 // TODO We should make this much more selective in the future so we only return true when we
-// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though
-// we can just assume that we care we are loaded at all.
-//
-// Even if we don't keep track of this at the method level we might want to keep track of it at the
-// level of enabled capabilities.
-bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(
-    art::ArtMethod* method ATTRIBUTE_UNUSED) {
-  return true;
+// actually care about the method at this time (ie active frames had locals changed). For now we
+// just assume that if anything has changed any frame's locals we care about all methods. If nothing
+// has we only care about methods with active breakpoints on them. In the future we should probably
+// rewrite all of this to instead do this at the ShadowFrame or thread granularity.
+bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) {
+  // Non-java-debuggable runtimes we need to assume that any method might not be debuggable and
+  // therefore potentially being inspected (due to inlines). If we are debuggable we rely hard on
+  // inlining not being done since we don't keep track of which methods get inlined where and simply
+  // look to see if the method is breakpointed.
+  return !art::Runtime::Current()->IsJavaDebuggable() ||
+      manager_->HaveLocalsChanged() ||
+      manager_->MethodHasBreakpoints(method);
 }
 
 bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) {
   return !manager_->MethodHasBreakpoints(method);
 }
 
+bool JvmtiMethodInspectionCallback::MethodNeedsDebugVersion(
+    art::ArtMethod* method ATTRIBUTE_UNUSED) {
+  return true;
+}
+
 DeoptManager::DeoptManager()
   : deoptimization_status_lock_("JVMTI_DeoptimizationStatusLock",
                                 static_cast<art::LockLevel>(
@@ -75,7 +84,10 @@
     performing_deoptimization_(false),
     global_deopt_count_(0),
     deopter_count_(0),
-    inspection_callback_(this) { }
+    breakpoint_status_lock_("JVMTI_BreakpointStatusLock",
+                            static_cast<art::LockLevel>(art::LockLevel::kAbortLock + 1)),
+    inspection_callback_(this),
+    set_local_variable_called_(false) { }
 
 void DeoptManager::Setup() {
   art::ScopedThreadStateChange stsc(art::Thread::Current(),
@@ -121,14 +133,11 @@
 }
 
 bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) {
-  art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_);
+  art::MutexLock lk(art::Thread::Current(), breakpoint_status_lock_);
   return MethodHasBreakpointsLocked(method);
 }
 
 bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) {
-  if (deopter_count_ == 0) {
-    return false;
-  }
   auto elem = breakpoint_status_.find(method);
   return elem != breakpoint_status_.end() && elem->second != 0;
 }
@@ -158,18 +167,23 @@
 
   art::ScopedThreadSuspension sts(self, art::kSuspended);
   deoptimization_status_lock_.ExclusiveLock(self);
+  {
+    breakpoint_status_lock_.ExclusiveLock(self);
 
-  DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
+    DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
 
-  if (MethodHasBreakpointsLocked(method)) {
-    // Don't need to do anything extra.
-    breakpoint_status_[method]++;
-    // Another thread might be deoptimizing the very method we just added new breakpoints for. Wait
-    // for any deopts to finish before moving on.
-    WaitForDeoptimizationToFinish(self);
-    return;
+    if (MethodHasBreakpointsLocked(method)) {
+      // Don't need to do anything extra.
+      breakpoint_status_[method]++;
+      // Another thread might be deoptimizing the very method we just added new breakpoints for.
+      // Wait for any deopts to finish before moving on.
+      breakpoint_status_lock_.ExclusiveUnlock(self);
+      WaitForDeoptimizationToFinish(self);
+      return;
+    }
+    breakpoint_status_[method] = 1;
+    breakpoint_status_lock_.ExclusiveUnlock(self);
   }
-  breakpoint_status_[method] = 1;
   auto instrumentation = art::Runtime::Current()->GetInstrumentation();
   if (instrumentation->IsForcedInterpretOnly()) {
     // We are already interpreting everything so no need to do anything.
@@ -196,17 +210,22 @@
   // need but since that is very heavy we will instead just use a condition variable to make sure we
   // don't race with ourselves.
   deoptimization_status_lock_.ExclusiveLock(self);
+  bool is_last_breakpoint;
+  {
+    art::MutexLock mu(self, breakpoint_status_lock_);
 
-  DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
-  DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without "
-                                             << "breakpoints present!";
+    DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
+    DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without "
+                                              << "breakpoints present!";
+    breakpoint_status_[method] -= 1;
+    is_last_breakpoint = (breakpoint_status_[method] == 0);
+  }
   auto instrumentation = art::Runtime::Current()->GetInstrumentation();
-  breakpoint_status_[method] -= 1;
   if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) {
     // We don't need to do anything since we are interpreting everything anyway.
     deoptimization_status_lock_.ExclusiveUnlock(self);
     return;
-  } else if (breakpoint_status_[method] == 0) {
+  } else if (is_last_breakpoint) {
     if (UNLIKELY(is_default)) {
       RemoveDeoptimizeAllMethodsLocked(self);
     } else {
diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h
index a495b68..6e991de 100644
--- a/openjdkjvmti/deopt_manager.h
+++ b/openjdkjvmti/deopt_manager.h
@@ -32,6 +32,7 @@
 #ifndef ART_OPENJDKJVMTI_DEOPT_MANAGER_H_
 #define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_
 
+#include <atomic>
 #include <unordered_map>
 
 #include "jni.h"
@@ -62,6 +63,9 @@
   bool IsMethodSafeToJit(art::ArtMethod* method)
       OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_);
 
+  bool MethodNeedsDebugVersion(art::ArtMethod* method)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_);
+
  private:
   DeoptManager* manager_;
 };
@@ -107,9 +111,17 @@
 
   static DeoptManager* Get();
 
+  bool HaveLocalsChanged() const {
+    return set_local_variable_called_.load();
+  }
+
+  void SetLocalsUpdated() {
+    set_local_variable_called_.store(true);
+  }
+
  private:
   bool MethodHasBreakpointsLocked(art::ArtMethod* method)
-      REQUIRES(deoptimization_status_lock_);
+      REQUIRES(breakpoint_status_lock_);
 
   // Wait until nothing is currently in the middle of deoptimizing/undeoptimizing something. This is
   // needed to ensure that everything is synchronized since threads need to drop the
@@ -156,13 +168,20 @@
   // Number of users of deoptimization there currently are.
   uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_);
 
+  // A mutex that just protects the breakpoint-status map. This mutex should always be at the
+  // bottom of the lock hierarchy. Nothing more should be locked if we hold this.
+  art::Mutex breakpoint_status_lock_ ACQUIRED_BEFORE(art::Locks::abort_lock_);
   // A map from methods to the number of breakpoints in them from all envs.
   std::unordered_map<art::ArtMethod*, uint32_t> breakpoint_status_
-      GUARDED_BY(deoptimization_status_lock_);
+      GUARDED_BY(breakpoint_status_lock_);
 
   // The MethodInspectionCallback we use to tell the runtime if we care about particular methods.
   JvmtiMethodInspectionCallback inspection_callback_;
 
+  // Set to true if anything calls SetLocalVariables on any thread since we need to be careful about
+  // OSR after this.
+  std::atomic<bool> set_local_variable_called_;
+
   // Helper for setting up/tearing-down for deoptimization.
   friend class ScopedDeoptimizationContext;
 };
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index bf2e6cd..b83310d 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -915,6 +915,9 @@
   if (depth < 0) {
     return ERR(ILLEGAL_ARGUMENT);
   }
+  // Make sure that we know not to do any OSR anymore.
+  // TODO We should really keep track of this at the Frame granularity.
+  DeoptManager::Get()->SetLocalsUpdated();
   art::Thread* self = art::Thread::Current();
   // Suspend JIT since it can get confused if we deoptimize methods getting jitted.
   art::jit::ScopedJitSuspend suspend_jit;
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index 41a649b..4526be4 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -925,7 +925,9 @@
     if (target != self) {
       called_method = true;
       // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
-      if (!target->RequestSynchronousCheckpoint(&closure)) {
+      // Since this deals with object references we need to avoid going to sleep.
+      art::ScopedAssertNoThreadSuspension sants("Getting owned monitor usage");
+      if (!target->RequestSynchronousCheckpoint(&closure, art::ThreadState::kRunnable)) {
         return ERR(THREAD_NOT_ALIVE);
       }
     } else {
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 4736fd3..9271a05 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -32,12 +32,10 @@
         "art_field.cc",
         "art_method.cc",
         "barrier.cc",
-        "base/arena_allocator.cc",
-        "base/arena_bit_vector.cc",
         "base/file_utils.cc",
+        "base/mem_map_arena_pool.cc",
         "base/mutex.cc",
         "base/quasi_atomic.cc",
-        "base/scoped_arena_allocator.cc",
         "base/timing_logger.cc",
         "cha.cc",
         "check_jni.cc",
@@ -46,6 +44,7 @@
         "class_table.cc",
         "common_throws.cc",
         "compiler_filter.cc",
+        "debug_print.cc",
         "debugger.cc",
         "dex/art_dex_file_loader.cc",
         "dex/dex_file_annotations.cc",
@@ -87,6 +86,7 @@
         "gc/space/zygote_space.cc",
         "gc/task_processor.cc",
         "gc/verification.cc",
+        "hidden_api.cc",
         "hprof/hprof.cc",
         "image.cc",
         "index_bss_mapping.cc",
@@ -121,7 +121,6 @@
         "linear_alloc.cc",
         "managed_stack.cc",
         "mem_map.cc",
-        "memory_region.cc",
         "method_handles.cc",
         "mirror/array.cc",
         "mirror/call_site.cc",
@@ -540,7 +539,6 @@
         "arch/x86/instruction_set_features_x86_test.cc",
         "arch/x86_64/instruction_set_features_x86_64_test.cc",
         "barrier_test.cc",
-        "base/arena_allocator_test.cc",
         "base/file_utils_test.cc",
         "base/mutex_test.cc",
         "base/timing_logger_test.cc",
@@ -584,7 +582,6 @@
         "java_vm_ext_test.cc",
         "jit/profile_compilation_info_test.cc",
         "mem_map_test.cc",
-        "memory_region_test.cc",
         "method_handles_test.cc",
         "mirror/dex_cache_test.cc",
         "mirror/method_type_test.cc",
diff --git a/runtime/base/dumpable-inl.h b/runtime/base/dumpable-inl.h
deleted file mode 100644
index 9d7fc39..0000000
--- a/runtime/base/dumpable-inl.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2016 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_RUNTIME_BASE_DUMPABLE_INL_H_
-#define ART_RUNTIME_BASE_DUMPABLE_INL_H_
-
-#include "base/dumpable.h"
-#include "base/mutex.h"
-#include "thread-current-inl.h"
-
-namespace art {
-
-template<typename T>
-inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs) {
-  Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
-  rhs.Dump(os);
-  return os;
-}
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_BASE_DUMPABLE_INL_H_
diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc
new file mode 100644
index 0000000..d5ea19b
--- /dev/null
+++ b/runtime/base/mem_map_arena_pool.cc
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "mem_map_arena_pool.h"
+
+#include <sys/mman.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <iomanip>
+#include <numeric>
+
+#include <android-base/logging.h>
+
+#include "base/arena_allocator-inl.h"
+#include "base/systrace.h"
+#include "mem_map.h"
+
+namespace art {
+
+class MemMapArena FINAL : public Arena {
+ public:
+  MemMapArena(size_t size, bool low_4gb, const char* name);
+  virtual ~MemMapArena();
+  void Release() OVERRIDE;
+
+ private:
+  std::unique_ptr<MemMap> map_;
+};
+
+MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) {
+  // Round up to a full page as that's the smallest unit of allocation for mmap()
+  // and we want to be able to use all memory that we actually allocate.
+  size = RoundUp(size, kPageSize);
+  std::string error_msg;
+  map_.reset(MemMap::MapAnonymous(
+      name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg));
+  CHECK(map_.get() != nullptr) << error_msg;
+  memory_ = map_->Begin();
+  static_assert(ArenaAllocator::kArenaAlignment <= kPageSize,
+                "Arena should not need stronger alignment than kPageSize.");
+  DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
+  size_ = map_->Size();
+}
+
+MemMapArena::~MemMapArena() {
+  // Destroys MemMap via std::unique_ptr<>.
+}
+
+void MemMapArena::Release() {
+  if (bytes_allocated_ > 0) {
+    map_->MadviseDontNeedAndZero();
+    bytes_allocated_ = 0;
+  }
+}
+
+MemMapArenaPool::MemMapArenaPool(bool low_4gb, const char* name)
+    : low_4gb_(low_4gb),
+      name_(name),
+      free_arenas_(nullptr) {
+  MemMap::Init();
+}
+
+MemMapArenaPool::~MemMapArenaPool() {
+  ReclaimMemory();
+}
+
+void MemMapArenaPool::ReclaimMemory() {
+  while (free_arenas_ != nullptr) {
+    Arena* arena = free_arenas_;
+    free_arenas_ = free_arenas_->next_;
+    delete arena;
+  }
+}
+
+void MemMapArenaPool::LockReclaimMemory() {
+  std::lock_guard<std::mutex> lock(lock_);
+  ReclaimMemory();
+}
+
+Arena* MemMapArenaPool::AllocArena(size_t size) {
+  Arena* ret = nullptr;
+  {
+    std::lock_guard<std::mutex> lock(lock_);
+    if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
+      ret = free_arenas_;
+      free_arenas_ = free_arenas_->next_;
+    }
+  }
+  if (ret == nullptr) {
+    ret = new MemMapArena(size, low_4gb_, name_);
+  }
+  ret->Reset();
+  return ret;
+}
+
+void MemMapArenaPool::TrimMaps() {
+  ScopedTrace trace(__PRETTY_FUNCTION__);
+  std::lock_guard<std::mutex> lock(lock_);
+  for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
+    arena->Release();
+  }
+}
+
+size_t MemMapArenaPool::GetBytesAllocated() const {
+  size_t total = 0;
+  std::lock_guard<std::mutex> lock(lock_);
+  for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
+    total += arena->GetBytesAllocated();
+  }
+  return total;
+}
+
+void MemMapArenaPool::FreeArenaChain(Arena* first) {
+  if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+    for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
+      MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
+    }
+  }
+
+  if (arena_allocator::kArenaAllocatorPreciseTracking) {
+    // Do not reuse arenas when tracking.
+    while (first != nullptr) {
+      Arena* next = first->next_;
+      delete first;
+      first = next;
+    }
+    return;
+  }
+
+  if (first != nullptr) {
+    Arena* last = first;
+    while (last->next_ != nullptr) {
+      last = last->next_;
+    }
+    std::lock_guard<std::mutex> lock(lock_);
+    last->next_ = free_arenas_;
+    free_arenas_ = first;
+  }
+}
+
+}  // namespace art
diff --git a/runtime/base/mem_map_arena_pool.h b/runtime/base/mem_map_arena_pool.h
new file mode 100644
index 0000000..24e150e
--- /dev/null
+++ b/runtime/base/mem_map_arena_pool.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 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_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_
+#define ART_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_
+
+#include "base/arena_allocator.h"
+
+namespace art {
+
+class MemMapArenaPool FINAL : public ArenaPool {
+ public:
+  explicit MemMapArenaPool(bool low_4gb = false, const char* name = "LinearAlloc");
+  virtual ~MemMapArenaPool();
+  Arena* AllocArena(size_t size) OVERRIDE;
+  void FreeArenaChain(Arena* first) OVERRIDE;
+  size_t GetBytesAllocated() const OVERRIDE;
+  void ReclaimMemory() OVERRIDE;
+  void LockReclaimMemory() OVERRIDE;
+  // Trim the maps in arenas by madvising, used by JIT to reduce memory usage.
+  void TrimMaps() OVERRIDE;
+
+ private:
+  const bool low_4gb_;
+  const char* name_;
+  Arena* free_arenas_;
+  // Use a std::mutex here as Arenas are second-from-the-bottom when using MemMaps, and MemMap
+  // itself uses std::mutex scoped to within an allocate/free only.
+  mutable std::mutex lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemMapArenaPool);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_
diff --git a/runtime/base/mutator_locked_dumpable.h b/runtime/base/mutator_locked_dumpable.h
new file mode 100644
index 0000000..cf2199c
--- /dev/null
+++ b/runtime/base/mutator_locked_dumpable.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 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_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_
+#define ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_
+
+#include "base/mutex.h"
+#include "thread-current-inl.h"
+
+namespace art {
+
+template<typename T>
+class MutatorLockedDumpable {
+ public:
+  explicit MutatorLockedDumpable(T& value) REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {}
+
+  void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    value_.Dump(os);
+  }
+
+ private:
+  const T& value_;
+
+  DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable);
+};
+
+// template<typename T>
+// std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs)
+//   // TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis
+//   //       currently fails for this.
+//     NO_THREAD_SAFETY_ANALYSIS;
+
+template<typename T>
+inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs)
+    NO_THREAD_SAFETY_ANALYSIS {
+  Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+  rhs.Dump(os);
+  return os;
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_
diff --git a/runtime/base/quasi_atomic.h b/runtime/base/quasi_atomic.h
index 067d01d..0012f64 100644
--- a/runtime/base/quasi_atomic.h
+++ b/runtime/base/quasi_atomic.h
@@ -152,14 +152,6 @@
     return NeedSwapMutexes(isa);
   }
 
-  static void ThreadFenceAcquire() {
-    std::atomic_thread_fence(std::memory_order_acquire);
-  }
-
-  static void ThreadFenceRelease() {
-    std::atomic_thread_fence(std::memory_order_release);
-  }
-
   static void ThreadFenceForConstructor() {
     #if defined(__aarch64__)
       __asm__ __volatile__("dmb ishst" : : : "memory");
@@ -168,10 +160,6 @@
     #endif
   }
 
-  static void ThreadFenceSequentiallyConsistent() {
-    std::atomic_thread_fence(std::memory_order_seq_cst);
-  }
-
  private:
   static Mutex* GetSwapMutex(const volatile int64_t* addr);
   static int64_t SwapMutexRead64(volatile const int64_t* addr);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2110d04..5e69efe 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -53,6 +53,7 @@
 #include "class_loader_utils.h"
 #include "class_table-inl.h"
 #include "compiler_callbacks.h"
+#include "debug_print.h"
 #include "debugger.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
@@ -72,6 +73,7 @@
 #include "gc/space/space-inl.h"
 #include "gc_root-inl.h"
 #include "handle_scope-inl.h"
+#include "hidden_api.h"
 #include "image-inl.h"
 #include "imt_conflict_table.h"
 #include "imtable-inl.h"
@@ -6178,7 +6180,7 @@
   // Note that there is a race in the presence of multiple threads and we may leak
   // memory from the LinearAlloc, but that's a tradeoff compared to using
   // atomic operations.
-  QuasiAtomic::ThreadFenceRelease();
+  std::atomic_thread_fence(std::memory_order_release);
   new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_);
   return new_conflict_method;
 }
@@ -7843,83 +7845,6 @@
   return resolved;
 }
 
-std::string DescribeSpace(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
-  std::ostringstream oss;
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  gc::space::ContinuousSpace* cs = heap->FindContinuousSpaceFromAddress(klass.Ptr());
-  if (cs != nullptr) {
-    if (cs->IsImageSpace()) {
-      oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename();
-    } else {
-      oss << "continuous;" << cs->GetName();
-    }
-  } else {
-    gc::space::DiscontinuousSpace* ds =
-        heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true);
-    if (ds != nullptr) {
-      oss << "discontinuous;" << ds->GetName();
-    } else {
-      oss << "invalid";
-    }
-  }
-  return oss.str();
-}
-
-std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  std::ostringstream oss;
-  uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor);
-  ObjPtr<mirror::Class> path_class_loader =
-      WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader);
-  ObjPtr<mirror::Class> dex_class_loader =
-      WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader);
-  ObjPtr<mirror::Class> delegate_last_class_loader =
-      WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
-
-  // Print the class loader chain.
-  bool found_class = false;
-  const char* loader_separator = "";
-  if (loader == nullptr) {
-    oss << "BootClassLoader";  // This would be unexpected.
-  }
-  for (; loader != nullptr; loader = loader->GetParent()) {
-    oss << loader_separator << loader->GetClass()->PrettyDescriptor();
-    loader_separator = ";";
-    // If we didn't find the interface yet, try to find it in the current class loader.
-    if (!found_class) {
-      ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader);
-      ObjPtr<mirror::Class> klass =
-          (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr;
-      if (klass != nullptr) {
-        found_class = true;
-        oss << "[hit:" << DescribeSpace(klass) << "]";
-      }
-    }
-
-    // For PathClassLoader, DexClassLoader or DelegateLastClassLoader
-    // also dump the dex file locations.
-    if (loader->GetClass() == path_class_loader ||
-        loader->GetClass() == dex_class_loader ||
-        loader->GetClass() == delegate_last_class_loader) {
-      oss << "(";
-      ScopedObjectAccessUnchecked soa(Thread::Current());
-      StackHandleScope<1> hs(soa.Self());
-      Handle<mirror::ClassLoader> handle(hs.NewHandle(loader));
-      const char* path_separator = "";
-      VisitClassLoaderDexFiles(soa,
-                               handle,
-                               [&](const DexFile* dex_file) {
-                                 oss << path_separator << dex_file->GetLocation();
-                                 path_separator = ":";
-                                 return true;  // Continue with the next DexFile.
-                               });
-      oss << ")";
-    }
-  }
-
-  return oss.str();
-}
-
 ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,
                                            ObjPtr<mirror::DexCache> dex_cache,
                                            ObjPtr<mirror::ClassLoader> class_loader,
@@ -8916,6 +8841,15 @@
   }
 }
 
+void ClassLinker::VisitAllocators(AllocatorVisitor* visitor) const {
+  for (const ClassLoaderData& data : class_loaders_) {
+    LinearAlloc* alloc = data.allocator;
+    if (alloc != nullptr && !visitor->Visit(alloc)) {
+        break;
+    }
+  }
+}
+
 void ClassLinker::InsertDexFileInToClassLoader(ObjPtr<mirror::Object> dex_file,
                                                ObjPtr<mirror::ClassLoader> class_loader) {
   DCHECK(dex_file != nullptr);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 2f6b754..fa70f65 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -97,6 +97,14 @@
       REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0;
 };
 
+class AllocatorVisitor {
+ public:
+  virtual ~AllocatorVisitor() {}
+  // Return true to continue visiting.
+  virtual bool Visit(LinearAlloc* alloc)
+      REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0;
+};
+
 class ClassLinker {
  public:
   // Well known mirror::Class roots accessed via GetClassRoot.
@@ -664,6 +672,11 @@
       REQUIRES(!Locks::classlinker_classes_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Visit all of the allocators that belong to classloaders except boot classloader.
+  // This is used by 616-cha-unloading test to confirm memory reuse.
+  void VisitAllocators(AllocatorVisitor* visitor) const
+      REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+
   // Throw the class initialization failure recorded when first trying to initialize the given
   // class.
   void ThrowEarlierClassFailure(ObjPtr<mirror::Class> c, bool wrap_in_no_class_def = false)
diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc
new file mode 100644
index 0000000..44a1836
--- /dev/null
+++ b/runtime/debug_print.cc
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <sstream>
+
+#include "debug_print.h"
+
+#include "class_linker.h"
+#include "class_table.h"
+#include "class_loader_utils.h"
+#include "dex/utf.h"
+#include "gc/heap.h"
+#include "gc/space/space-inl.h"
+#include "mirror/class.h"
+#include "mirror/class_loader.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+std::string DescribeSpace(ObjPtr<mirror::Class> klass) {
+  std::ostringstream oss;
+  gc::Heap* heap = Runtime::Current()->GetHeap();
+  gc::space::ContinuousSpace* cs =
+      heap->FindContinuousSpaceFromObject(klass.Ptr(), /* fail_ok */ true);
+  if (cs != nullptr) {
+    if (cs->IsImageSpace()) {
+      oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename();
+    } else {
+      oss << "continuous;" << cs->GetName();
+    }
+  } else {
+    gc::space::DiscontinuousSpace* ds =
+        heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true);
+    if (ds != nullptr) {
+      oss << "discontinuous;" << ds->GetName();
+    } else {
+      oss << "invalid";
+    }
+  }
+  return oss.str();
+}
+
+std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor) {
+  std::ostringstream oss;
+  uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor);
+  ObjPtr<mirror::Class> path_class_loader =
+      WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader);
+  ObjPtr<mirror::Class> dex_class_loader =
+      WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader);
+  ObjPtr<mirror::Class> delegate_last_class_loader =
+      WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+
+  // Print the class loader chain.
+  bool found_class = false;
+  const char* loader_separator = "";
+  if (loader == nullptr) {
+    oss << "BootClassLoader";  // This would be unexpected.
+  }
+  for (; loader != nullptr; loader = loader->GetParent()) {
+    oss << loader_separator << loader->GetClass()->PrettyDescriptor();
+    loader_separator = ";";
+    // If we didn't find the class yet, try to find it in the current class loader.
+    if (!found_class) {
+      ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader);
+      ObjPtr<mirror::Class> klass =
+          (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr;
+      if (klass != nullptr) {
+        found_class = true;
+        oss << "[hit:" << DescribeSpace(klass) << "]";
+      }
+    }
+
+    // For PathClassLoader, DexClassLoader or DelegateLastClassLoader
+    // also dump the dex file locations.
+    if (loader->GetClass() == path_class_loader ||
+        loader->GetClass() == dex_class_loader ||
+        loader->GetClass() == delegate_last_class_loader) {
+      oss << "(";
+      ScopedObjectAccessUnchecked soa(Thread::Current());
+      StackHandleScope<1> hs(soa.Self());
+      Handle<mirror::ClassLoader> handle(hs.NewHandle(loader));
+      const char* path_separator = "";
+      VisitClassLoaderDexFiles(soa,
+                               handle,
+                               [&](const DexFile* dex_file) {
+                                 oss << path_separator << dex_file->GetLocation();
+                                 path_separator = ":";
+                                 return true;  // Continue with the next DexFile.
+                               });
+      oss << ")";
+    }
+  }
+
+  return oss.str();
+}
+
+}  // namespace art
diff --git a/runtime/debug_print.h b/runtime/debug_print.h
new file mode 100644
index 0000000..479c36a
--- /dev/null
+++ b/runtime/debug_print.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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_RUNTIME_DEBUG_PRINT_H_
+#define ART_RUNTIME_DEBUG_PRINT_H_
+
+#include "base/mutex.h"
+#include "mirror/object.h"
+
+// Helper functions for printing extra information for certain hard to diagnose bugs.
+
+namespace art {
+
+std::string DescribeSpace(ObjPtr<mirror::Class> klass)
+    REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_DEBUG_PRINT_H_
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 99a4c77..28659cb 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -364,6 +364,11 @@
   return !Dbg::MethodHasAnyBreakpoints(m);
 }
 
+bool DebuggerActiveMethodInspectionCallback::MethodNeedsDebugVersion(
+    ArtMethod* m ATTRIBUTE_UNUSED) {
+  return Dbg::IsDebuggerActive();
+}
+
 void InternalDebuggerControlCallback::StartDebugger() {
   // Release the mutator lock.
   ScopedThreadStateChange stsc(art::Thread::Current(), kNative);
@@ -4354,9 +4359,11 @@
           WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch,
           type, dataArray.get(), 0, data.size()));
   if (env->ExceptionCheck()) {
-    LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type);
-    env->ExceptionDescribe();
-    env->ExceptionClear();
+    Thread* self = Thread::Current();
+    ScopedObjectAccess soa(self);
+    LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type) << std::endl
+              << self->GetException()->Dump();
+    self->ClearException();
     return false;
   }
 
@@ -4400,10 +4407,11 @@
                           reinterpret_cast<jbyte*>(out_data->data()));
 
   if (env->ExceptionCheck()) {
+    Thread* self = Thread::Current();
+    ScopedObjectAccess soa(self);
     LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x",
-                              type);
-    env->ExceptionDescribe();
-    env->ExceptionClear();
+                              type) << std::endl << self->GetException()->Dump();
+    self->ClearException();
     return false;
   }
 
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 7401813..e1de991 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -56,6 +56,7 @@
 struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback {
   bool IsMethodBeingInspected(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
   bool IsMethodSafeToJit(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+  bool MethodNeedsDebugVersion(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
 };
 
 struct DebuggerDdmCallback : public DdmCallback {
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2284100..39429c5 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -19,6 +19,7 @@
 #include "base/enums.h"
 #include "callee_save_frame.h"
 #include "common_throws.h"
+#include "debug_print.h"
 #include "debugger.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_types.h"
@@ -1187,9 +1188,24 @@
   }
 }
 
+static void DumpB74410240ClassData(ObjPtr<mirror::Class> klass)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  std::string storage;
+  const char* descriptor = klass->GetDescriptor(&storage);
+  LOG(FATAL_WITHOUT_ABORT) << "  " << DescribeLoaders(klass->GetClassLoader(), descriptor);
+  const OatDexFile* oat_dex_file = klass->GetDexFile().GetOatDexFile();
+  if (oat_dex_file != nullptr) {
+    const OatFile* oat_file = oat_dex_file->GetOatFile();
+    const char* dex2oat_cmdline =
+        oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
+    LOG(FATAL_WITHOUT_ABORT) << "    OatFile: " << oat_file->GetLocation()
+        << "; " << (dex2oat_cmdline != nullptr ? dex2oat_cmdline : "<not recorded>");
+  }
+}
+
 static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) {
   // Mimick the search for the caller and dump some data while doing so.
-  LOG(FATAL_WITHOUT_ABORT) << "Dumping debugging data for b/74410240.";
+  LOG(FATAL_WITHOUT_ABORT) << "Dumping debugging data, please attach a bugreport to b/74410240.";
 
   constexpr CalleeSaveType type = CalleeSaveType::kSaveRefsAndArgs;
   CHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type));
@@ -1227,6 +1243,7 @@
       << " dex pc: " << dex_pc
       << " dex file: " << outer_method->GetDexFile()->GetLocation()
       << " class table: " << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader());
+  DumpB74410240ClassData(outer_method->GetDeclaringClass());
   LOG(FATAL_WITHOUT_ABORT) << "  instruction: " << DumpInstruction(outer_method, dex_pc);
 
   ArtMethod* caller = outer_method;
@@ -1260,7 +1277,8 @@
           << " dex pc: " << dex_pc
           << " dex file: " << caller->GetDexFile()->GetLocation()
           << " class table: "
-          << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader());
+          << class_linker->ClassTableForClassLoader(caller->GetClassLoader());
+      DumpB74410240ClassData(caller->GetDeclaringClass());
       LOG(FATAL_WITHOUT_ABORT) << "  instruction: " << DumpInstruction(caller, dex_pc);
     }
   }
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 3015b10..671079b 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -37,7 +37,9 @@
 // Static fault manger object accessed by signal handler.
 FaultManager fault_manager;
 
-extern "C" __attribute__((visibility("default"))) void art_sigsegv_fault() {
+// This needs to be NO_INLINE since some debuggers do not read the inline-info to set a breakpoint
+// if it isn't.
+extern "C" NO_INLINE __attribute__((visibility("default"))) void art_sigsegv_fault() {
   // Set a breakpoint here to be informed when a SIGSEGV is unhandled by ART.
   VLOG(signals)<< "Caught unknown SIGSEGV in ART fault handler - chaining to next handler.";
 }
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index bb5167f..0747c3c 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2357,14 +2357,13 @@
   size_t non_moving_space_bytes_allocated = 0U;
   size_t bytes_allocated = 0U;
   size_t dummy;
+  bool fall_back_to_non_moving = false;
   mirror::Object* to_ref = region_space_->AllocNonvirtual</*kForEvac*/ true>(
       region_space_alloc_size, &region_space_bytes_allocated, nullptr, &dummy);
   bytes_allocated = region_space_bytes_allocated;
-  if (to_ref != nullptr) {
+  if (LIKELY(to_ref != nullptr)) {
     DCHECK_EQ(region_space_alloc_size, region_space_bytes_allocated);
-  }
-  bool fall_back_to_non_moving = false;
-  if (UNLIKELY(to_ref == nullptr)) {
+  } else {
     // Failed to allocate in the region space. Try the skipped blocks.
     to_ref = AllocateInSkippedBlock(region_space_alloc_size);
     if (to_ref != nullptr) {
@@ -2374,6 +2373,9 @@
         region_space_->RecordAlloc(to_ref);
       }
       bytes_allocated = region_space_alloc_size;
+      heap_->num_bytes_allocated_.fetch_sub(bytes_allocated, std::memory_order_seq_cst);
+      to_space_bytes_skipped_.fetch_sub(bytes_allocated, std::memory_order_seq_cst);
+      to_space_objects_skipped_.fetch_sub(1, std::memory_order_seq_cst);
     } else {
       // Fall back to the non-moving space.
       fall_back_to_non_moving = true;
@@ -2383,7 +2385,6 @@
                   << " skipped_objects="
                   << to_space_objects_skipped_.load(std::memory_order_seq_cst);
       }
-      fall_back_to_non_moving = true;
       to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size,
                                                &non_moving_space_bytes_allocated, nullptr, &dummy);
       if (UNLIKELY(to_ref == nullptr)) {
@@ -2471,7 +2472,7 @@
 
     // Do a fence to prevent the field CAS in ConcurrentCopying::Process from possibly reordering
     // before the object copy.
-    QuasiAtomic::ThreadFenceRelease();
+    std::atomic_thread_fence(std::memory_order_release);
 
     LockWord new_lock_word = LockWord::FromForwardingAddress(reinterpret_cast<size_t>(to_ref));
 
@@ -2566,7 +2567,7 @@
 
 bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) {
   // TODO: Explain why this is here. What release operation does it pair with?
-  QuasiAtomic::ThreadFenceAcquire();
+  std::atomic_thread_fence(std::memory_order_acquire);
   accounting::ObjectStack* alloc_stack = GetAllocationStack();
   return alloc_stack->Contains(ref);
 }
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 247e25c..f16138c 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3730,13 +3730,21 @@
   task_processor_->AddTask(self, added_task);
 }
 
+void Heap::IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke) {
+  size_t previous_num_bytes_freed_revoke =
+      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
+  // Check the updated value is less than the number of bytes allocated. There is a risk of
+  // execution being suspended between the increment above and the CHECK below, leading to
+  // the use of previous_num_bytes_freed_revoke in the comparison.
+  CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
+           previous_num_bytes_freed_revoke + freed_bytes_revoke);
+}
+
 void Heap::RevokeThreadLocalBuffers(Thread* thread) {
   if (rosalloc_space_ != nullptr) {
     size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread);
     if (freed_bytes_revoke > 0U) {
-      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
-      CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
-               num_bytes_freed_revoke_.load(std::memory_order_relaxed));
+      IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);
     }
   }
   if (bump_pointer_space_ != nullptr) {
@@ -3751,9 +3759,7 @@
   if (rosalloc_space_ != nullptr) {
     size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread);
     if (freed_bytes_revoke > 0U) {
-      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
-      CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
-               num_bytes_freed_revoke_.load(std::memory_order_relaxed));
+      IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);
     }
   }
 }
@@ -3762,9 +3768,7 @@
   if (rosalloc_space_ != nullptr) {
     size_t freed_bytes_revoke = rosalloc_space_->RevokeAllThreadLocalBuffers();
     if (freed_bytes_revoke > 0U) {
-      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
-      CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
-               num_bytes_freed_revoke_.load(std::memory_order_relaxed));
+      IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);
     }
   }
   if (bump_pointer_space_ != nullptr) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9af57d1..ef1c088 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1091,6 +1091,8 @@
     return max_free_;
   }
 
+  ALWAYS_INLINE void IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke);
+
   void TraceHeapSize(size_t heap_size);
 
   // Remove a vlog code from heap-inl.h which is transitively included in half the world.
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
new file mode 100644
index 0000000..f0b36a0
--- /dev/null
+++ b/runtime/hidden_api.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "hidden_api.h"
+
+#include "base/dumpable.h"
+
+namespace art {
+namespace hiddenapi {
+
+static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
+  switch (value) {
+    case kReflection:
+      os << "reflection";
+      break;
+    case kJNI:
+      os << "JNI";
+      break;
+    case kLinking:
+      os << "linking";
+      break;
+  }
+  return os;
+}
+
+static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) {
+  return static_cast<int>(policy) == static_cast<int>(apiList);
+}
+
+// GetMemberAction-related static_asserts.
+static_assert(
+    EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) &&
+    EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) &&
+    EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist),
+    "Mismatch between EnforcementPolicy and ApiList enums");
+static_assert(
+    EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList &&
+    EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
+    "EnforcementPolicy values ordering not correct");
+
+namespace detail {
+
+MemberSignature::MemberSignature(ArtField* field) {
+  member_type_ = "field";
+  signature_parts_ = {
+    field->GetDeclaringClass()->GetDescriptor(&tmp_),
+    "->",
+    field->GetName(),
+    ":",
+    field->GetTypeDescriptor()
+  };
+}
+
+MemberSignature::MemberSignature(ArtMethod* method) {
+  member_type_ = "method";
+  signature_parts_ = {
+    method->GetDeclaringClass()->GetDescriptor(&tmp_),
+    "->",
+    method->GetName(),
+    method->GetSignature().ToString()
+  };
+}
+
+bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const {
+  size_t pos = 0;
+  for (const std::string& part : signature_parts_) {
+    size_t count = std::min(prefix.length() - pos, part.length());
+    if (prefix.compare(pos, count, part, 0, count) == 0) {
+      pos += count;
+    } else {
+      return false;
+    }
+  }
+  // We have a complete match if all parts match (we exit the loop without
+  // returning) AND we've matched the whole prefix.
+  return pos == prefix.length();
+}
+
+bool MemberSignature::IsExempted(const std::vector<std::string>& exemptions) {
+  for (const std::string& exemption : exemptions) {
+    if (DoesPrefixMatch(exemption)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void MemberSignature::Dump(std::ostream& os) const {
+  for (std::string part : signature_parts_) {
+    os << part;
+  }
+}
+
+void MemberSignature::WarnAboutAccess(AccessMethod access_method,
+                                      HiddenApiAccessFlags::ApiList list) {
+  LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable<MemberSignature>(*this)
+               << " (" << list << ", " << access_method << ")";
+}
+
+template<typename T>
+bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access_method) {
+  // Get the signature, we need it later.
+  MemberSignature member_signature(member);
+
+  Runtime* runtime = Runtime::Current();
+
+  if (action == kDeny) {
+    // If we were about to deny, check for an exemption first.
+    // Exempted APIs are treated as light grey list.
+    if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
+      action = kAllowButWarn;
+      // Avoid re-examining the exemption list next time.
+      // Note this results in the warning below showing "light greylist", which
+      // seems like what one would expect. Exemptions effectively add new members to
+      // the light greylist.
+      member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+              member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist));
+    }
+  }
+
+  // Print a log message with information about this class member access.
+  // We do this regardless of whether we block the access or not.
+  member_signature.WarnAboutAccess(access_method,
+      HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags()));
+
+  if (action == kDeny) {
+    // Block access
+    return true;
+  }
+
+  // Allow access to this member but print a warning.
+  DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
+
+  // Depending on a runtime flag, we might move the member into whitelist and
+  // skip the warning the next time the member is accessed.
+  if (runtime->ShouldDedupeHiddenApiWarnings()) {
+    member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+        member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+  }
+
+  // If this action requires a UI warning, set the appropriate flag.
+  if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
+    runtime->SetPendingHiddenApiWarning(true);
+  }
+
+  return false;
+}
+
+// Need to instantiate this.
+template bool ShouldBlockAccessToMemberImpl<ArtField>(ArtField* member,
+                                                      Action action,
+                                                      AccessMethod access_method);
+template bool ShouldBlockAccessToMemberImpl<ArtMethod>(ArtMethod* member,
+                                                       Action action,
+                                                       AccessMethod access_method);
+
+}  // namespace detail
+}  // namespace hiddenapi
+}  // namespace art
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 0e6f159..cc6c146 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -19,7 +19,7 @@
 
 #include "art_field-inl.h"
 #include "art_method-inl.h"
-#include "base/dumpable.h"
+#include "base/mutex.h"
 #include "dex/hidden_api_access_flags.h"
 #include "mirror/class-inl.h"
 #include "reflection.h"
@@ -58,25 +58,6 @@
   kLinking,
 };
 
-inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
-  switch (value) {
-    case kReflection:
-      os << "reflection";
-      break;
-    case kJNI:
-      os << "JNI";
-      break;
-    case kLinking:
-      os << "linking";
-      break;
-  }
-  return os;
-}
-
-static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) {
-  return static_cast<int>(policy) == static_cast<int>(apiList);
-}
-
 inline Action GetMemberAction(uint32_t access_flags) {
   EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
   if (policy == EnforcementPolicy::kNoChecks) {
@@ -89,16 +70,7 @@
     return kAllow;
   }
   // The logic below relies on equality of values in the enums EnforcementPolicy and
-  // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected.
-  static_assert(
-      EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) &&
-      EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) &&
-      EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist),
-      "Mismatch between EnforcementPolicy and ApiList enums");
-  static_assert(
-      EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList &&
-      EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
-      "EnforcementPolicy values ordering not correct");
+  // HiddenApiAccessFlags::ApiList, and their ordering. Assertions are in hidden_api.cc.
   if (static_cast<int>(policy) > static_cast<int>(api_list)) {
     return api_list == HiddenApiAccessFlags::kDarkGreylist
         ? kAllowButWarnAndToast
@@ -108,6 +80,8 @@
   }
 }
 
+// Implementation details. DO NOT ACCESS DIRECTLY.
+namespace detail {
 
 // Class to encapsulate the signature of a member (ArtField or ArtMethod). This
 // is used as a helper when matching prefixes, and when logging the signature.
@@ -118,69 +92,45 @@
   std::string tmp_;
 
  public:
-  explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
-    member_type_ = "field";
-    signature_parts_ = {
-      field->GetDeclaringClass()->GetDescriptor(&tmp_),
-      "->",
-      field->GetName(),
-      ":",
-      field->GetTypeDescriptor()
-    };
-  }
+  explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_);
+  explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
-    member_type_ = "method";
-    signature_parts_ = {
-      method->GetDeclaringClass()->GetDescriptor(&tmp_),
-      "->",
-      method->GetName(),
-      method->GetSignature().ToString()
-    };
-  }
+  void Dump(std::ostream& os) const;
 
-  const std::vector<std::string>& Parts() const {
-    return signature_parts_;
-  }
-
-  void Dump(std::ostream& os) const {
-    for (std::string part : signature_parts_) {
-      os << part;
-    }
-  }
   // Performs prefix match on this member. Since the full member signature is
   // composed of several parts, we match each part in turn (rather than
   // building the entire thing in memory and performing a simple prefix match)
-  bool DoesPrefixMatch(const std::string& prefix) const {
-    size_t pos = 0;
-    for (const std::string& part : signature_parts_) {
-      size_t count = std::min(prefix.length() - pos, part.length());
-      if (prefix.compare(pos, count, part, 0, count) == 0) {
-        pos += count;
-      } else {
-        return false;
-      }
-    }
-    // We have a complete match if all parts match (we exit the loop without
-    // returning) AND we've matched the whole prefix.
-    return pos == prefix.length();
-  }
+  bool DoesPrefixMatch(const std::string& prefix) const;
 
-  bool IsExempted(const std::vector<std::string>& exemptions) {
-    for (const std::string& exemption : exemptions) {
-      if (DoesPrefixMatch(exemption)) {
-        return true;
-      }
-    }
-    return false;
-  }
+  bool IsExempted(const std::vector<std::string>& exemptions);
 
-  void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list) {
-    LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable<MemberSignature>(*this)
-                 << " (" << list << ", " << access_method << ")";
-  }
+  void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list);
 };
 
+template<typename T>
+bool ShouldBlockAccessToMemberImpl(T* member,
+                                   Action action,
+                                   AccessMethod access_method)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
+// Returns true if the caller is either loaded by the boot strap class loader or comes from
+// a dex file located in ${ANDROID_ROOT}/framework/.
+ALWAYS_INLINE
+inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader,
+                                  ObjPtr<mirror::DexCache> caller_dex_cache)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (caller_class_loader.IsNull()) {
+    return true;
+  } else if (caller_dex_cache.IsNull()) {
+    return false;
+  } else {
+    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
+    return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile();
+  }
+}
+
+}  // namespace detail
+
 // Returns true if access to `member` should be denied to the caller of the
 // reflective query. The decision is based on whether the caller is in the
 // platform or not. Because different users of this function determine this
@@ -209,72 +159,13 @@
   }
 
   // Member is hidden and caller is not in the platform.
-
-  // Get the signature, we need it later.
-  MemberSignature member_signature(member);
-
-  Runtime* runtime = Runtime::Current();
-
-  if (action == kDeny) {
-    // If we were about to deny, check for an exemption first.
-    // Exempted APIs are treated as light grey list.
-    if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
-      action = kAllowButWarn;
-      // Avoid re-examining the exemption list next time.
-      // Note this results in the warning below showing "light greylist", which
-      // seems like what one would expect. Exemptions effectively add new members to
-      // the light greylist.
-      member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
-              member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist));
-    }
-  }
-
-  // Print a log message with information about this class member access.
-  // We do this regardless of whether we block the access or not.
-  member_signature.WarnAboutAccess(access_method,
-      HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags()));
-
-  if (action == kDeny) {
-    // Block access
-    return true;
-  }
-
-  // Allow access to this member but print a warning.
-  DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
-
-  // Depending on a runtime flag, we might move the member into whitelist and
-  // skip the warning the next time the member is accessed.
-  if (runtime->ShouldDedupeHiddenApiWarnings()) {
-    member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
-        member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
-  }
-
-  // If this action requires a UI warning, set the appropriate flag.
-  if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
-    runtime->SetPendingHiddenApiWarning(true);
-  }
-
-  return false;
-}
-
-// Returns true if the caller is either loaded by the boot strap class loader or comes from
-// a dex file located in ${ANDROID_ROOT}/framework/.
-inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader,
-                                  ObjPtr<mirror::DexCache> caller_dex_cache)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (caller_class_loader.IsNull()) {
-    return true;
-  } else if (caller_dex_cache.IsNull()) {
-    return false;
-  } else {
-    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
-    return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile();
-  }
+  return detail::ShouldBlockAccessToMemberImpl(member, action, access_method);
 }
 
 inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  return !caller.IsNull() && IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());
+  return !caller.IsNull() &&
+      detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());
 }
 
 // Returns true if access to `member` should be denied to a caller loaded with
@@ -286,7 +177,7 @@
                                       ObjPtr<mirror::DexCache> caller_dex_cache,
                                       AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  bool caller_in_platform = IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);
+  bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);
   return ShouldBlockAccessToMember(member,
                                    /* thread */ nullptr,
                                    [caller_in_platform] (Thread*) { return caller_in_platform; },
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 190d2cb..5a31dd4 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -21,6 +21,8 @@
 
 namespace art {
 
+using hiddenapi::detail::MemberSignature;
+
 class HiddenApiTest : public CommonRuntimeTest {
  protected:
   void SetUp() OVERRIDE {
@@ -102,172 +104,172 @@
 TEST_F(HiddenApiTest, CheckEverythingMatchesL) {
   ScopedObjectAccess soa(self_);
   std::string prefix("L");
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class3_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class3_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class3_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class3_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckPackageMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/");
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class3_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class3_method1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class3_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class3_method1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckClassMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1");
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckClassExactMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;");
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckMethodMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;->method1");
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckMethodExactMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;->method1(");
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckMethodSignatureMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;->method1(I)");
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckMethodSignatureAndReturnMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;->method1()V");
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckFieldMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;->field1");
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckFieldExactMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;->field1:");
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckFieldTypeMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;->field1:I");
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckConstructorMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;-><init>");
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckConstructorExactMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;-><init>()V");
-  ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckMethodSignatureTrailingCharsNoMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;->method1()Vfoo");
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckConstructorTrailingCharsNoMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;-><init>()Vfoo");
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
 }
 
 TEST_F(HiddenApiTest, CheckFieldTrailingCharsNoMatch) {
   ScopedObjectAccess soa(self_);
   std::string prefix("Lmypackage/packagea/Class1;->field1:Ifoo");
-  ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+  ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
 }
 
 }  // namespace art
diff --git a/runtime/image.cc b/runtime/image.cc
index f147078..316f7a5 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '8', '\0' };  // R^3 Bitstring type check.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '9', '\0' };  // ReachabilityFence.
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 3b9cc0f..6143ba6 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -16,7 +16,7 @@
 
 #include "indirect_reference_table-inl.h"
 
-#include "base/dumpable-inl.h"
+#include "base/mutator_locked_dumpable.h"
 #include "base/systrace.h"
 #include "base/utils.h"
 #include "java_vm_ext.h"
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 84a148f..d7f33d5 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -139,10 +139,13 @@
 
 bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  return Runtime::Current()->IsJavaDebuggable() &&
+  art::Runtime* runtime = Runtime::Current();
+  // If anything says we need the debug version or we are debuggable we will need the debug version
+  // of the method.
+  return (runtime->GetRuntimeCallbacks()->MethodNeedsDebugVersion(method) ||
+          runtime->IsJavaDebuggable()) &&
          !method->IsNative() &&
-         !method->IsProxyMethod() &&
-         Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method);
+         !method->IsProxyMethod();
 }
 
 void Instrumentation::InstallStubsForMethod(ArtMethod* method) {
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 681a582..69dae31 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -91,7 +91,7 @@
 // java.lang.Integer.signum(I)I
 UNARY_INTRINSIC(MterpIntegerSignum, Signum, GetVReg, SetI);
 
-// java.lang.Long.reverse(I)I
+// java.lang.Long.reverse(J)J
 UNARY_INTRINSIC(MterpLongReverse, ReverseBits64, GetVRegLong, SetJ);
 
 // java.lang.Long.reverseBytes(J)J
@@ -399,6 +399,16 @@
 VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetPlain)
 VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetRelease)
 
+static ALWAYS_INLINE bool MterpReachabilityFence(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED,
+                                                 const Instruction* inst ATTRIBUTE_UNUSED,
+                                                 uint16_t inst_data ATTRIBUTE_UNUSED,
+                                                 JValue* result_register ATTRIBUTE_UNUSED)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // Do nothing; Its only purpose is to keep the argument reference live
+  // at preceding suspend points. That's automatic in the interpreter.
+  return true;
+}
+
 // Macro to help keep track of what's left to implement.
 #define UNIMPLEMENTED_CASE(name)    \
     case Intrinsics::k##name:       \
@@ -499,6 +509,7 @@
     UNIMPLEMENTED_CASE(MemoryPokeIntNative /* (JI)V */)
     UNIMPLEMENTED_CASE(MemoryPokeLongNative /* (JJ)V */)
     UNIMPLEMENTED_CASE(MemoryPokeShortNative /* (JS)V */)
+    INTRINSIC_CASE(ReachabilityFence /* (Ljava/lang/Object;)V */)
     INTRINSIC_CASE(StringCharAt)
     INTRINSIC_CASE(StringCompareTo)
     INTRINSIC_CASE(StringEquals)
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 4a2dd3b..4c7a97d 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1538,7 +1538,7 @@
   }
   int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2);
   mirror::Object* newValue = shadow_frame->GetVRegReference(arg_offset + 4);
-  QuasiAtomic::ThreadFenceRelease();
+  std::atomic_thread_fence(std::memory_order_release);
   if (Runtime::Current()->IsActiveTransaction()) {
     obj->SetFieldObject<true>(MemberOffset(offset), newValue);
   } else {
diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h
index da08793..2f91f5d 100644
--- a/runtime/intrinsics_list.h
+++ b/runtime/intrinsics_list.h
@@ -218,6 +218,7 @@
   V(VarHandleReleaseFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "releaseFence", "()V") \
   V(VarHandleLoadLoadFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "loadLoadFence", "()V") \
   V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \
+  V(ReachabilityFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/ref/Reference;", "reachabilityFence", "(Ljava/lang/Object;)V") \
   SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V)
 
 #endif  // ART_RUNTIME_INTRINSICS_LIST_H_
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 5618b6e..de64fdd 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1539,7 +1539,7 @@
 
   // Make sure other threads see the data in the profiling info object before the
   // store in the ArtMethod's ProfilingInfo pointer.
-  QuasiAtomic::ThreadFenceRelease();
+  std::atomic_thread_fence(std::memory_order_release);
 
   method->SetProfilingInfo(info);
   profiling_infos_.push_back(info);
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 1bbce4f..6bbe78f 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -34,9 +34,8 @@
 
 #include "base/arena_allocator.h"
 #include "base/dumpable.h"
-#include "base/file_utils.h"
 #include "base/logging.h"  // For VLOG.
-#include "base/mutex.h"
+#include "base/malloc_arena_pool.h"
 #include "base/os.h"
 #include "base/safe_map.h"
 #include "base/scoped_flock.h"
@@ -90,7 +89,7 @@
 }
 
 ProfileCompilationInfo::ProfileCompilationInfo()
-    : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"),
+    : default_arena_pool_(),
       allocator_(&default_arena_pool_),
       info_(allocator_.Adapter(kArenaAllocProfile)),
       profile_key_map_(std::less<const std::string>(), allocator_.Adapter(kArenaAllocProfile)) {
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 6c56db9..f8334ce 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -23,8 +23,9 @@
 #include "base/arena_containers.h"
 #include "base/arena_object.h"
 #include "base/atomic.h"
+#include "base/bit_memory_region.h"
+#include "base/malloc_arena_pool.h"
 #include "base/safe_map.h"
-#include "bit_memory_region.h"
 #include "dex/dex_cache_resolved_classes.h"
 #include "dex/dex_file.h"
 #include "dex/dex_file_types.h"
@@ -792,7 +793,7 @@
   friend class ProfileAssistantTest;
   friend class Dex2oatLayoutTest;
 
-  ArenaPool default_arena_pool_;
+  MallocArenaPool default_arena_pool_;
   ArenaAllocator allocator_;
 
   // Vector containing the actual profile info.
diff --git a/runtime/linear_alloc.h b/runtime/linear_alloc.h
index 384b2e3..87086f2 100644
--- a/runtime/linear_alloc.h
+++ b/runtime/linear_alloc.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_LINEAR_ALLOC_H_
 
 #include "base/arena_allocator.h"
+#include "base/mutex.h"
 
 namespace art {
 
diff --git a/runtime/method_info.h b/runtime/method_info.h
index fe06256..b00ddc6 100644
--- a/runtime/method_info.h
+++ b/runtime/method_info.h
@@ -21,7 +21,7 @@
 
 #include "base/leb128.h"
 #include "base/macros.h"
-#include "memory_region.h"
+#include "base/memory_region.h"
 
 namespace art {
 
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index f0898f4..72b3179 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -31,7 +31,6 @@
 #include "dex/invoke_type.h"
 #include "dex_cache.h"
 #include "gc/heap-inl.h"
-#include "hidden_api.h"
 #include "iftable.h"
 #include "subtype_check.h"
 #include "object-inl.h"
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index e110763..f246d8b 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1101,7 +1101,7 @@
       case LockWord::kFatLocked: {
         // We should have done an acquire read of the lockword initially, to ensure
         // visibility of the monitor data structure. Use an explicit fence instead.
-        QuasiAtomic::ThreadFenceAcquire();
+        std::atomic_thread_fence(std::memory_order_acquire);
         Monitor* mon = lock_word.FatLockMonitor();
         if (trylock) {
           return mon->TryLock(self) ? h_obj.Get() : nullptr;
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index d9a5096..cf0a72a 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -27,6 +27,7 @@
 #include "base/mutex.h"
 #include "base/runtime_debug.h"
 #include "debugger.h"
+#include "hidden_api.h"
 #include "java_vm_ext.h"
 #include "jit/jit.h"
 #include "jni_internal.h"
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 25f984f..fb00ae3 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -116,7 +116,7 @@
   ScopedFastNativeObjectAccess soa(env);
   ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   // TODO: A release store is likely to be faster on future processors.
-  QuasiAtomic::ThreadFenceRelease();
+  std::atomic_thread_fence(std::memory_order_release);
   // JNI must use non transactional mode.
   obj->SetField32<false>(MemberOffset(offset), newValue);
 }
@@ -152,7 +152,7 @@
                                   jlong newValue) {
   ScopedFastNativeObjectAccess soa(env);
   ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
-  QuasiAtomic::ThreadFenceRelease();
+  std::atomic_thread_fence(std::memory_order_release);
   // JNI must use non transactional mode.
   obj->SetField64<false>(MemberOffset(offset), newValue);
 }
@@ -194,7 +194,7 @@
   ScopedFastNativeObjectAccess soa(env);
   ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
-  QuasiAtomic::ThreadFenceRelease();
+  std::atomic_thread_fence(std::memory_order_release);
   // JNI must use non transactional mode.
   obj->SetFieldObject<false>(MemberOffset(offset), newValue);
 }
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 9a626ba..4068158 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -62,6 +62,8 @@
 #include "base/dumpable.h"
 #include "base/enums.h"
 #include "base/file_utils.h"
+#include "base/malloc_arena_pool.h"
+#include "base/mem_map_arena_pool.h"
 #include "base/memory_tool.h"
 #include "base/mutex.h"
 #include "base/os.h"
@@ -86,6 +88,7 @@
 #include "gc/space/space-inl.h"
 #include "gc/system_weak.h"
 #include "handle_scope-inl.h"
+#include "hidden_api.h"
 #include "image-inl.h"
 #include "instrumentation.h"
 #include "intern_table.h"
@@ -1331,13 +1334,17 @@
   // Use MemMap arena pool for jit, malloc otherwise. Malloc arenas are faster to allocate but
   // can't be trimmed as easily.
   const bool use_malloc = IsAotCompiler();
-  arena_pool_.reset(new ArenaPool(use_malloc, /* low_4gb */ false));
-  jit_arena_pool_.reset(
-      new ArenaPool(/* use_malloc */ false, /* low_4gb */ false, "CompilerMetadata"));
+  if (use_malloc) {
+    arena_pool_.reset(new MallocArenaPool());
+    jit_arena_pool_.reset(new MallocArenaPool());
+  } else {
+    arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ false));
+    jit_arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ false, "CompilerMetadata"));
+  }
 
   if (IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA)) {
     // 4gb, no malloc. Explanation in header.
-    low_4gb_arena_pool_.reset(new ArenaPool(/* use_malloc */ false, /* low_4gb */ true));
+    low_4gb_arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ true));
   }
   linear_alloc_.reset(CreateLinearAlloc());
 
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index cd3c0b7..758917c 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -106,6 +106,15 @@
   return false;
 }
 
+bool RuntimeCallbacks::MethodNeedsDebugVersion(ArtMethod* m) {
+  for (MethodInspectionCallback* cb : method_inspection_callbacks_) {
+    if (cb->MethodNeedsDebugVersion(m)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
   thread_callbacks_.push_back(cb);
 }
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 24386ba..9f0410d 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -130,6 +130,10 @@
   // Note that '!IsMethodSafeToJit(m) implies IsMethodBeingInspected(m)'. That is that if this
   // method returns false IsMethodBeingInspected must return true.
   virtual bool IsMethodSafeToJit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+  // Returns true if we expect the method to be debuggable but are not doing anything unusual with
+  // it currently.
+  virtual bool MethodNeedsDebugVersion(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 };
 
 class RuntimeCallbacks {
@@ -198,6 +202,11 @@
   // entrypoint should not be changed to JITed code.
   bool IsMethodSafeToJit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Returns true if some MethodInspectionCallback indicates the method needs to use a debug
+  // version. This allows later code to set breakpoints or perform other actions that could be
+  // broken by some optimizations.
+  bool MethodNeedsDebugVersion(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
   void AddMethodInspectionCallback(MethodInspectionCallback* cb)
       REQUIRES_SHARED(Locks::mutator_lock_);
   void RemoveMethodInspectionCallback(MethodInspectionCallback* cb)
diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc
index 59af918..eae2505 100644
--- a/runtime/runtime_common.cc
+++ b/runtime/runtime_common.cc
@@ -371,30 +371,11 @@
 #pragma GCC diagnostic ignored "-Wframe-larger-than="
 #endif
 
-void HandleUnexpectedSignalCommon(int signal_number,
-                                  siginfo_t* info,
-                                  void* raw_context,
-                                  bool handle_timeout_signal,
-                                  bool dump_on_stderr) {
-  static bool handling_unexpected_signal = false;
-  if (handling_unexpected_signal) {
-    LogHelper::LogLineLowStack(__FILE__,
-                               __LINE__,
-                               ::android::base::FATAL_WITHOUT_ABORT,
-                               "HandleUnexpectedSignal reentered\n");
-    if (handle_timeout_signal) {
-      if (IsTimeoutSignal(signal_number)) {
-        // Ignore a recursive timeout.
-        return;
-      }
-    }
-    _exit(1);
-  }
-  handling_unexpected_signal = true;
-
-  gAborting++;  // set before taking any locks
-  MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_);
-
+static void HandleUnexpectedSignalCommonDump(int signal_number,
+                                             siginfo_t* info,
+                                             void* raw_context,
+                                             bool handle_timeout_signal,
+                                             bool dump_on_stderr) {
   auto logger = [&](auto& stream) {
     bool has_address = (signal_number == SIGILL || signal_number == SIGBUS ||
                         signal_number == SIGFPE || signal_number == SIGSEGV);
@@ -453,6 +434,71 @@
   }
 }
 
+void HandleUnexpectedSignalCommon(int signal_number,
+                                  siginfo_t* info,
+                                  void* raw_context,
+                                  bool handle_timeout_signal,
+                                  bool dump_on_stderr) {
+  // Local _static_ storing the currently handled signal (or -1).
+  static int handling_unexpected_signal = -1;
+
+  // Whether the dump code should be run under the unexpected-signal lock. For diagnostics we
+  // allow recursive unexpected-signals in certain cases - avoid a deadlock.
+  bool grab_lock = true;
+
+  if (handling_unexpected_signal != -1) {
+    LogHelper::LogLineLowStack(__FILE__,
+                               __LINE__,
+                               ::android::base::FATAL_WITHOUT_ABORT,
+                               "HandleUnexpectedSignal reentered\n");
+    // Print the signal number. Don't use any standard functions, just some arithmetic. Just best
+    // effort, with a minimal buffer.
+    if (0 < signal_number && signal_number < 100) {
+      char buf[] = { ' ',
+                     'S',
+                     static_cast<char>('0' + (signal_number / 10)),
+                     static_cast<char>('0' + (signal_number % 10)),
+                     '\n',
+                     0 };
+      LogHelper::LogLineLowStack(__FILE__,
+                                 __LINE__,
+                                 ::android::base::FATAL_WITHOUT_ABORT,
+                                 buf);
+    }
+    if (handle_timeout_signal) {
+      if (IsTimeoutSignal(signal_number)) {
+        // Ignore a recursive timeout.
+        return;
+      }
+    }
+    // If we were handling a timeout signal, try to go on. Otherwise hard-exit.
+    // This relies on the expectation that we'll only ever get one timeout signal.
+    if (!handle_timeout_signal || handling_unexpected_signal != GetTimeoutSignal()) {
+      _exit(1);
+    }
+    grab_lock = false;  // The "outer" handling instance already holds the lock.
+  }
+  handling_unexpected_signal = signal_number;
+
+  gAborting++;  // set before taking any locks
+
+  if (grab_lock) {
+    MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_);
+
+    HandleUnexpectedSignalCommonDump(signal_number,
+                                     info,
+                                     raw_context,
+                                     handle_timeout_signal,
+                                     dump_on_stderr);
+  } else {
+    HandleUnexpectedSignalCommonDump(signal_number,
+                                     info,
+                                     raw_context,
+                                     handle_timeout_signal,
+                                     dump_on_stderr);
+  }
+}
+
 #if defined(__APPLE__)
 #pragma GCC diagnostic pop
 #endif
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index bde3462..3839764 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -20,12 +20,12 @@
 #include <limits>
 
 #include "arch/code_offset.h"
+#include "base/bit_memory_region.h"
 #include "base/bit_utils.h"
 #include "base/bit_vector.h"
 #include "base/leb128.h"
-#include "bit_memory_region.h"
+#include "base/memory_region.h"
 #include "dex/dex_file_types.h"
-#include "memory_region.h"
 #include "method_info.h"
 
 namespace art {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index b13d8ec..d17f409 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2881,6 +2881,17 @@
 
   Handle<mirror::Class> h_aste_class(hs.NewHandle<mirror::Class>(
       h_aste_array_class->GetComponentType()));
+
+  // Make sure the AnnotatedStackTraceElement.class is initialized, b/76208924 .
+  class_linker->EnsureInitialized(soa.Self(),
+                                  h_aste_class,
+                                  /* can_init_fields */ true,
+                                  /* can_init_parents */ true);
+  if (soa.Self()->IsExceptionPending()) {
+    // This should not fail in a healthy runtime.
+    return nullptr;
+  }
+
   ArtField* stack_trace_element_field = h_aste_class->FindField(
       soa.Self(), h_aste_class.Get(), "stackTraceElement", "Ljava/lang/StackTraceElement;");
   DCHECK(stack_trace_element_field != nullptr);
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index ec4dc41..838d7f1 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -317,6 +317,7 @@
   if (quickening_info.empty()) {
     return ArrayRef<const uint8_t>();
   }
+  CHECK_LT(dex_method_idx, dex_file.NumMethodIds());
   const uint32_t quickening_offset =
       GetQuickenInfoOffsetTable(dex_file, quickening_info).GetOffset(dex_method_idx);
   if (quickening_offset == 0u) {
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 18ad6b5..168eb7b 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -22,6 +22,7 @@
 
 #include <android-base/logging.h>
 
+#include "base/mutex.h"
 #include "base/safe_map.h"
 #include "base/scoped_arena_containers.h"
 
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 51d4a81..be7ae4a 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
@@ -80,6 +81,7 @@
         // the test fail (even when keeping the `null` assignment). b/76454261
         FinalizerTest keepLive = wimp.get();
         System.out.println("wimp: " + wimpString(wimp));
+        Reference.reachabilityFence(keepLive);
         keepLive = null;  // Clear the reference.
 
         /* this will try to collect and finalize ft */
diff --git a/test/072-reachability-fence/expected.txt b/test/072-reachability-fence/expected.txt
new file mode 100644
index 0000000..fdd0d7b
--- /dev/null
+++ b/test/072-reachability-fence/expected.txt
@@ -0,0 +1,5 @@
+Starting
+Reference 0 was live.
+Reference 3 was live.
+Reference 4 was live.
+Finished
diff --git a/test/072-reachability-fence/info.txt b/test/072-reachability-fence/info.txt
new file mode 100644
index 0000000..21b6d6a
--- /dev/null
+++ b/test/072-reachability-fence/info.txt
@@ -0,0 +1,4 @@
+Check that reachabilityFence() prevents garbage collection of objects only referred to by a dead
+reference.
+
+This is not very convincing, since we currently usually keep such objects around anyway.
diff --git a/test/072-reachability-fence/src/Main.java b/test/072-reachability-fence/src/Main.java
new file mode 100644
index 0000000..ac1e131
--- /dev/null
+++ b/test/072-reachability-fence/src/Main.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+
+public class Main {
+    public static void main(String[] args) {
+        System.out.println("Starting");
+        WeakReference wrefs[] = new WeakReference[5];
+        String str0 = generateString("String", 0);
+        String str1 = generateString("String", 1);
+        String str2 = generateString("String", 2);
+        String str3 = generateString("String", 3);
+        String str4 = generateString("String", 4);
+        wrefs[0] = new WeakReference(str0);
+        wrefs[1] = new WeakReference(str1);
+        wrefs[2] = new WeakReference(str2);
+        wrefs[3] = new WeakReference(str3);
+        wrefs[4] = new WeakReference(str4);
+        // Clear a couple as a sanity check.
+        str1 = null;
+        str2 = null;
+        // str<n> dead here; in the future we will possibly reuse the registers.
+        // Give the compiler something to fill the registers with.
+        String str5 = generateString("String", 5);
+        String str6 = generateString("String", 6);
+        String str7 = generateString("String", 7);
+        String str8 = generateString("String", 8);
+        String str9 = generateString("String", 9);
+        Runtime.getRuntime().gc();
+        for (int i = 0; i < 5; ++i) {
+          if (wrefs[i].get() != null) {
+            System.out.println("Reference " + i + " was live.");
+          }
+        }
+        Reference.reachabilityFence(str0);
+        Reference.reachabilityFence(str1);
+        Reference.reachabilityFence(str2);
+        Reference.reachabilityFence(str3);
+        Reference.reachabilityFence(str4);
+        System.out.println("Finished");
+    }
+
+    private static String generateString(String base, int num) {
+        return base + num;
+    }
+}
diff --git a/test/1940-ddms-ext/check b/test/166-bad-interface-super/build
old mode 100755
new mode 100644
similarity index 64%
rename from test/1940-ddms-ext/check
rename to test/166-bad-interface-super/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/166-bad-interface-super/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/171-init-aste/expected.txt b/test/171-init-aste/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/171-init-aste/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/171-init-aste/info.txt b/test/171-init-aste/info.txt
new file mode 100644
index 0000000..201e8ad
--- /dev/null
+++ b/test/171-init-aste/info.txt
@@ -0,0 +1 @@
+Regression test for failure to initialize dalvik.system.AnnotatedStackTraceElement.
diff --git a/test/171-init-aste/src-art/Main.java b/test/171-init-aste/src-art/Main.java
new file mode 100644
index 0000000..9d36610
--- /dev/null
+++ b/test/171-init-aste/src-art/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+import java.lang.reflect.Method;
+import dalvik.system.AnnotatedStackTraceElement;
+
+public class Main {
+    public static void main(String args[]) throws Exception {
+        Class<?> vmStack = Class.forName("dalvik.system.VMStack");
+        Method getAnnotatedThreadStackTrace =
+                vmStack.getDeclaredMethod("getAnnotatedThreadStackTrace", Thread.class);
+        Object[] annotatedStackTrace =
+                (Object[]) getAnnotatedThreadStackTrace.invoke(null, Thread.currentThread());
+        AnnotatedStackTraceElement annotatedElement =
+            (AnnotatedStackTraceElement) annotatedStackTrace[0];
+        // This used to fail an assertion that the AnnotatedStackTraceElement.class
+        // is at least initializing (i.e. initializing, initialized or resolved-erroneous).
+        // Note: We cannot use reflection for this test because getDeclaredMethod() would
+        // initialize the class and hide the failure.
+        annotatedElement.getStackTraceElement();
+
+        System.out.println("passed");
+    }
+}
diff --git a/test/171-init-aste/src/Main.java b/test/171-init-aste/src/Main.java
new file mode 100644
index 0000000..4479cb4
--- /dev/null
+++ b/test/171-init-aste/src/Main.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+public class Main {
+    // Note: This file is used for the RI which does not support
+    // dalvik.system.AnnotatedStackTraceElement (see src-art/Main.java),
+    // so that we do not need an exclusion in known failures.
+    public static void main(String args[]) throws Exception {
+        System.out.println("passed");
+    }
+}
diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt
index cdb8f6a..a685891 100644
--- a/test/1935-get-set-current-frame-jit/expected.txt
+++ b/test/1935-get-set-current-frame-jit/expected.txt
@@ -1,7 +1,5 @@
 JNI_OnLoad called
 From GetLocalInt(), value is 42
-isInOsrCode? false
 	Value is '42'
 Setting TARGET to 1337
-isInOsrCode? false
 	Value is '1337'
diff --git a/test/1935-get-set-current-frame-jit/run b/test/1935-get-set-current-frame-jit/run
index 51875a7..e569d08 100755
--- a/test/1935-get-set-current-frame-jit/run
+++ b/test/1935-get-set-current-frame-jit/run
@@ -14,5 +14,5 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
+# Ensure the test is not subject to code collection
+./default-run "$@" --jvmti --runtime-option -Xjitinitialsize:32M
diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java
index 714a98a..378aaf7 100644
--- a/test/1935-get-set-current-frame-jit/src/Main.java
+++ b/test/1935-get-set-current-frame-jit/src/Main.java
@@ -21,6 +21,7 @@
 import java.lang.reflect.Executable;
 import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
+import java.time.Instant;
 import java.util.concurrent.Semaphore;
 import java.util.Arrays;
 import java.util.Collection;
@@ -49,9 +50,11 @@
   public static class IntRunner implements Runnable {
     private volatile boolean continueBusyLoop;
     private volatile boolean inBusyLoop;
-    public IntRunner() {
+    private final boolean expectOsr;
+    public IntRunner(boolean expectOsr) {
       this.continueBusyLoop = true;
       this.inBusyLoop = false;
+      this.expectOsr = expectOsr;
     }
     public void run() {
       int TARGET = 42;
@@ -59,14 +62,23 @@
       while (continueBusyLoop) {
         inBusyLoop = true;
       }
-      int i = 0;
-      while (Main.isInterpreted() && i < 10000) {
-        Main.ensureJitCompiled(IntRunner.class, "run");
-        i++;
-      }
-      // We shouldn't be doing OSR since we are using JVMTI and the get/set prevents OSR.
+      // Wait up to 300 seconds for OSR to kick in if we expect it. If we don't give up after only
+      // 3 seconds.
+      Instant osrDeadline = Instant.now().plusSeconds(expectOsr ? 600 : 3);
+      do {
+        // Don't actually do anything here.
+        inBusyLoop = true;
+      } while (hasJit() && !Main.isInOsrCode("run") && osrDeadline.compareTo(Instant.now()) > 0);
+      // We shouldn't be doing OSR since we are using JVMTI and the set prevents OSR.
       // Set local will also push us to interpreter but the get local may remain in compiled code.
-      System.out.println("isInOsrCode? " + (hasJit() && Main.isInOsrCode("run")));
+      if (hasJit()) {
+        boolean inOsr = Main.isInOsrCode("run");
+        if (expectOsr && !inOsr) {
+          throw new Error("Expected to be in OSR but was not.");
+        } else if (!expectOsr && inOsr) {
+          throw new Error("Expected not to be in OSR but was.");
+        }
+      }
       reportValue(TARGET);
     }
     public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
@@ -78,7 +90,7 @@
   public static void runGet() throws Exception {
     Method target = IntRunner.class.getDeclaredMethod("run");
     // Get Int
-    IntRunner int_runner = new IntRunner();
+    IntRunner int_runner = new IntRunner(true);
     Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
     target_get.start();
     int_runner.waitForBusyLoopStart();
@@ -108,7 +120,7 @@
   public static void runSet() throws Exception {
     Method target = IntRunner.class.getDeclaredMethod("run");
     // Set Int
-    IntRunner int_runner = new IntRunner();
+    IntRunner int_runner = new IntRunner(false);
     Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
     target_set.start();
     int_runner.waitForBusyLoopStart();
@@ -157,7 +169,6 @@
     throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
   }
 
-  public static native void ensureJitCompiled(Class k, String f);
   public static native boolean isInterpreted();
   public static native boolean isInOsrCode(String methodName);
   public static native boolean hasJit();
diff --git a/test/1940-ddms-ext/expected_error.txt b/test/1940-ddms-ext/expected_error.txt
deleted file mode 100644
index 73883b4..0000000
--- a/test/1940-ddms-ext/expected_error.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-java.lang.ArrayIndexOutOfBoundsException: byte[] offset=12 length=55 src.length=1
-	at art.Test1940.processChunk(Native Method)
-	at art.Test1940.run(Test1940.java:156)
-	at Main.main(Main.java:19)
diff --git a/test/1940-ddms-ext/remove_error.py b/test/1940-ddms-ext/remove_error.py
deleted file mode 100755
index 638c479..0000000
--- a/test/1940-ddms-ext/remove_error.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018 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.
-
-import argparse
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument('input_data', type=open)
-  parser.add_argument('expected_error', type=str)
-  args = parser.parse_args()
-
-  for line in map(str.rstrip, args.input_data.readlines()):
-    print_full = True
-    with open(args.expected_error) as err_file:
-      for err_line in map(str.rstrip, err_file):
-        if line.startswith(err_line):
-          print_full = False
-          if line != err_line:
-            print(line[len(err_line):])
-          break
-    if print_full and line != '':
-      print(line)
-
-if __name__ == '__main__':
-  main()
diff --git a/test/411-optimizing-arith-mul/info.txt b/test/411-optimizing-arith-mul/info.txt
deleted file mode 100644
index 1015551..0000000
--- a/test/411-optimizing-arith-mul/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for basic arithmethic operations.
diff --git a/test/411-optimizing-arith-mul/expected.txt b/test/411-optimizing-arith/expected.txt
similarity index 100%
rename from test/411-optimizing-arith-mul/expected.txt
rename to test/411-optimizing-arith/expected.txt
diff --git a/test/411-optimizing-arith/info.txt b/test/411-optimizing-arith/info.txt
new file mode 100644
index 0000000..42be5d5
--- /dev/null
+++ b/test/411-optimizing-arith/info.txt
@@ -0,0 +1,7 @@
+Tests for basic arithmethic operations:
+  - multiply,
+  - subtract,
+  - negate,
+  - division,
+  - modulo (rem),
+  - shifts.
diff --git a/test/417-optimizing-arith-div/src/Main.java b/test/411-optimizing-arith/src/DivTest.java
similarity index 98%
rename from test/417-optimizing-arith-div/src/Main.java
rename to test/411-optimizing-arith/src/DivTest.java
index 68e89b3..7696d0a 100644
--- a/test/417-optimizing-arith-div/src/Main.java
+++ b/test/411-optimizing-arith/src/DivTest.java
@@ -16,7 +16,7 @@
 
 // Note that $opt$ is a marker for the optimizing compiler to test
 // it does compile the method.
-public class Main {
+public class DivTest {
 
   public static void expectEquals(int expected, int result) {
     if (expected != result) {
@@ -98,11 +98,7 @@
     }
   }
 
-  public static void main(String[] args) {
-    div();
-  }
-
-  public static void div() {
+  public static void main() {
     divInt();
     divLong();
     divFloat();
diff --git a/test/411-optimizing-arith/src/Main.java b/test/411-optimizing-arith/src/Main.java
new file mode 100644
index 0000000..e1a43d3
--- /dev/null
+++ b/test/411-optimizing-arith/src/Main.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+public class Main {
+    public static void main(String args[]) {
+        MulTest.main();
+        SubTest.main();
+        NegTest.main();
+        DivTest.main();
+        RemTest.main();
+        ShiftsTest.main();
+    }
+}
diff --git a/test/411-optimizing-arith-mul/src/Main.java b/test/411-optimizing-arith/src/MulTest.java
similarity index 93%
rename from test/411-optimizing-arith-mul/src/Main.java
rename to test/411-optimizing-arith/src/MulTest.java
index 60e418e..b9bffca 100644
--- a/test/411-optimizing-arith-mul/src/Main.java
+++ b/test/411-optimizing-arith/src/MulTest.java
@@ -16,7 +16,7 @@
 
 // Note that $opt$ is a marker for the optimizing compiler to test
 // it does compile the method.
-public class Main {
+public class MulTest {
 
   public static void expectEquals(int expected, int result) {
     if (expected != result) {
@@ -72,11 +72,7 @@
     }
   }
 
-  public static void main(String[] args) {
-    mul();
-  }
-
-  public static void mul() {
+  public static void main() {
     mulInt();
     mulLong();
     mulFloat();
@@ -129,9 +125,12 @@
     expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(-2F, 3.40282346638528860e+38F));
     expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(2F, Float.NEGATIVE_INFINITY));
     expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(-2F, Float.NEGATIVE_INFINITY));
-    expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
-    expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
-    expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectEquals(Float.NEGATIVE_INFINITY,
+                 $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectEquals(Float.POSITIVE_INFINITY,
+                 $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+    expectEquals(Float.POSITIVE_INFINITY,
+                 $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
   }
 
   private static void mulDouble() {
diff --git a/test/415-optimizing-arith-neg/src/Main.java b/test/411-optimizing-arith/src/NegTest.java
similarity index 92%
rename from test/415-optimizing-arith-neg/src/Main.java
rename to test/411-optimizing-arith/src/NegTest.java
index c53b639..8304726 100644
--- a/test/415-optimizing-arith-neg/src/Main.java
+++ b/test/411-optimizing-arith/src/NegTest.java
@@ -17,7 +17,7 @@
 // Note that $opt$ is a marker for the optimizing compiler to test
 // it does compile the method, and that $noinline$ is a marker to
 // test that it does not inline it.
-public class Main {
+public class NegTest {
 
   public static void assertEquals(int expected, int result) {
     if (expected != result) {
@@ -67,7 +67,7 @@
     }
   }
 
-  public static void main(String[] args) {
+  public static void main() {
     negInt();
     $opt$noinline$InplaceNegOneInt(1);
 
@@ -169,55 +169,29 @@
   }
 
 
-  static boolean doThrow = false;
-
   private static void $opt$noinline$InplaceNegOneInt(int a) {
-    if (doThrow) {
-      // Try defeating inlining.
-      throw new Error();
-    }
     a = -a;
     assertEquals(-1, a);
   }
 
   private static void $opt$noinline$InplaceNegOneLong(long a) {
-    if (doThrow) {
-      // Try defeating inlining.
-      throw new Error();
-    }
     a = -a;
     assertEquals(-1L, a);
   }
 
   private static int $opt$noinline$NegInt(int a){
-    if (doThrow) {
-      // Try defeating inlining.
-      throw new Error();
-    }
     return -a;
   }
 
   private static long $opt$noinline$NegLong(long a){
-    if (doThrow) {
-      // Try defeating inlining.
-      throw new Error();
-    }
     return -a;
   }
 
   private static float $opt$noinline$NegFloat(float a){
-    if (doThrow) {
-      // Try defeating inlining.
-      throw new Error();
-    }
     return -a;
   }
 
   private static double $opt$noinline$NegDouble(double a){
-    if (doThrow) {
-      // Try defeating inlining.
-      throw new Error();
-    }
     return -a;
   }
 }
diff --git a/test/428-optimizing-arith-rem/src/Main.java b/test/411-optimizing-arith/src/RemTest.java
similarity index 98%
rename from test/428-optimizing-arith-rem/src/Main.java
rename to test/411-optimizing-arith/src/RemTest.java
index 3f77318..1b31f63 100644
--- a/test/428-optimizing-arith-rem/src/Main.java
+++ b/test/411-optimizing-arith/src/RemTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-public class Main {
+public class RemTest {
 
-  public static void main(String[] args) {
+  public static void main() {
     remInt();
     remLong();
   }
diff --git a/test/431-optimizing-arith-shifts/src/Main.java b/test/411-optimizing-arith/src/ShiftsTest.java
similarity index 98%
rename from test/431-optimizing-arith-shifts/src/Main.java
rename to test/411-optimizing-arith/src/ShiftsTest.java
index b7a112f..139ff70 100644
--- a/test/431-optimizing-arith-shifts/src/Main.java
+++ b/test/411-optimizing-arith/src/ShiftsTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-public class Main {
+public class ShiftsTest {
 
   public static void expectEquals(int expected, int result) {
     if (expected != result) {
@@ -28,7 +28,7 @@
     }
   }
 
-  public static void main(String[] args) {
+  public static void main() {
     testShlInt();
     testShlLong();
     testShrInt();
diff --git a/test/414-optimizing-arith-sub/src/Main.java b/test/411-optimizing-arith/src/SubTest.java
similarity index 98%
rename from test/414-optimizing-arith-sub/src/Main.java
rename to test/411-optimizing-arith/src/SubTest.java
index b4531cd..9c9ea92 100644
--- a/test/414-optimizing-arith-sub/src/Main.java
+++ b/test/411-optimizing-arith/src/SubTest.java
@@ -16,7 +16,7 @@
 
 // Note that $opt$ is a marker for the optimizing compiler to test
 // it does compile the method.
-public class Main {
+public class SubTest {
 
   public static void expectEquals(int expected, int result) {
     if (expected != result) {
@@ -70,7 +70,7 @@
     }
   }
 
-  public static void main(String[] args) {
+  public static void main() {
     subInt();
     subLong();
     subFloat();
diff --git a/test/414-optimizing-arith-sub/expected.txt b/test/414-optimizing-arith-sub/expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/test/414-optimizing-arith-sub/expected.txt
+++ /dev/null
diff --git a/test/414-optimizing-arith-sub/info.txt b/test/414-optimizing-arith-sub/info.txt
deleted file mode 100644
index 1eaa148..0000000
--- a/test/414-optimizing-arith-sub/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Subtraction tests.
diff --git a/test/415-optimizing-arith-neg/expected.txt b/test/415-optimizing-arith-neg/expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/test/415-optimizing-arith-neg/expected.txt
+++ /dev/null
diff --git a/test/415-optimizing-arith-neg/info.txt b/test/415-optimizing-arith-neg/info.txt
deleted file mode 100644
index 8494aad..0000000
--- a/test/415-optimizing-arith-neg/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for arithmetic negation operations.
diff --git a/test/417-optimizing-arith-div/expected.txt b/test/417-optimizing-arith-div/expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/test/417-optimizing-arith-div/expected.txt
+++ /dev/null
diff --git a/test/417-optimizing-arith-div/info.txt b/test/417-optimizing-arith-div/info.txt
deleted file mode 100644
index 1374b0f..0000000
--- a/test/417-optimizing-arith-div/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for division operation.
diff --git a/test/428-optimizing-arith-rem/expected.txt b/test/428-optimizing-arith-rem/expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/test/428-optimizing-arith-rem/expected.txt
+++ /dev/null
diff --git a/test/428-optimizing-arith-rem/info.txt b/test/428-optimizing-arith-rem/info.txt
deleted file mode 100644
index 3e37ffe..0000000
--- a/test/428-optimizing-arith-rem/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for modulo (rem) operation.
diff --git a/test/431-optimizing-arith-shifts/expected.txt b/test/431-optimizing-arith-shifts/expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/test/431-optimizing-arith-shifts/expected.txt
+++ /dev/null
diff --git a/test/431-optimizing-arith-shifts/info.txt b/test/431-optimizing-arith-shifts/info.txt
deleted file mode 100644
index 14ff264..0000000
--- a/test/431-optimizing-arith-shifts/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for shift operations.
diff --git a/test/1940-ddms-ext/check b/test/551-checker-shifter-operand/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/551-checker-shifter-operand/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/551-checker-shifter-operand/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/616-cha-unloading/cha_unload.cc b/test/616-cha-unloading/cha_unload.cc
index 4be3456..b17be6b 100644
--- a/test/616-cha-unloading/cha_unload.cc
+++ b/test/616-cha-unloading/cha_unload.cc
@@ -19,6 +19,7 @@
 #include <iostream>
 
 #include "art_method.h"
+#include "class_linker.h"
 #include "jit/jit.h"
 #include "linear_alloc.h"
 #include "nativehelper/ScopedUtfChars.h"
@@ -29,6 +30,22 @@
 namespace art {
 namespace {
 
+class FindPointerAllocatorVisitor : public AllocatorVisitor {
+ public:
+  explicit FindPointerAllocatorVisitor(void* ptr) : is_found(false), ptr_(ptr) {}
+
+  bool Visit(LinearAlloc* alloc)
+      REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
+    is_found = alloc->Contains(ptr_);
+    return !is_found;
+  }
+
+  bool is_found;
+
+ private:
+  void* ptr_;
+};
+
 extern "C" JNIEXPORT jlong JNICALL Java_Main_getArtMethod(JNIEnv* env,
                                                           jclass,
                                                           jobject java_method) {
@@ -40,13 +57,30 @@
 extern "C" JNIEXPORT void JNICALL Java_Main_reuseArenaOfMethod(JNIEnv*,
                                                                jclass,
                                                                jlong art_method) {
-  // Create a new allocation and use it to request a specified amount of arenas.
-  // Hopefully one of them is a reused one, the one that covers the art_method pointer.
+  void* ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(art_method));
+
+  ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_);
+  ReaderMutexLock mu2(Thread::Current(), *Locks::classlinker_classes_lock_);
+  // Check if the arena was already implicitly reused by boot classloader.
+  if (Runtime::Current()->GetLinearAlloc()->Contains(ptr)) {
+    return;
+  }
+
+  // Check if the arena was already implicitly reused by some other classloader.
+  FindPointerAllocatorVisitor visitor(ptr);
+  Runtime::Current()->GetClassLinker()->VisitAllocators(&visitor);
+  if (visitor.is_found) {
+    return;
+  }
+
+  // The arena was not reused yet. Do it explicitly.
+  // Create a new allocation and use it to request new arenas until one of them is
+  // a reused one that covers the art_method pointer.
   std::unique_ptr<LinearAlloc> alloc(Runtime::Current()->CreateLinearAlloc());
   do {
-    // Ask for a byte - it's sufficient to get an arena and not have issues with size.
+    // Ask for a byte - it's sufficient to get an arena.
     alloc->Alloc(Thread::Current(), 1);
-  } while (!alloc->Contains(reinterpret_cast<void*>(static_cast<uintptr_t>(art_method))));
+  } while (!alloc->Contains(ptr));
 }
 
 }  // namespace
diff --git a/test/1940-ddms-ext/check b/test/626-checker-arm64-scratch-register/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/626-checker-arm64-scratch-register/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/626-checker-arm64-scratch-register/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/645-checker-abs-simd/src/Main.java b/test/645-checker-abs-simd/src/Main.java
index 870a403..819304a 100644
--- a/test/645-checker-abs-simd/src/Main.java
+++ b/test/645-checker-abs-simd/src/Main.java
@@ -48,10 +48,7 @@
   }
 
   /// CHECK-START: void Main.doitChar(char[]) loop_optimization (before)
-  /// CHECK-DAG: Phi       loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: ArrayGet  loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: Abs       loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: ArraySet  loop:<<Loop>>      outer_loop:none
+  /// CHECK-NOT: Abs
   //
   /// CHECK-START: void Main.doitChar(char[]) loop_optimization (after)
   /// CHECK-NOT: VecAbs
diff --git a/test/1940-ddms-ext/check b/test/646-checker-hadd-alt-char/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/646-checker-hadd-alt-char/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/646-checker-hadd-alt-char/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/646-checker-hadd-alt-short/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/646-checker-hadd-alt-short/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/646-checker-hadd-alt-short/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/646-checker-hadd-char/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/646-checker-hadd-char/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/646-checker-hadd-char/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/646-checker-hadd-short/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/646-checker-hadd-short/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/646-checker-hadd-short/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java
index 85c2fca..c09da81 100644
--- a/test/646-checker-hadd-short/src/Main.java
+++ b/test/646-checker-hadd-short/src/Main.java
@@ -26,6 +26,10 @@
   static short[] sB2 = new short[M];
   static short[] sBo = new short[M];
 
+  private static int $inline$mone() {
+    return -1;
+  }
+
   /// CHECK-START: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (before)
   /// CHECK-DAG: <<I1:i\d+>>   IntConstant 1                       loop:none
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
@@ -184,6 +188,35 @@
     }
   }
 
+  /// CHECK-START: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (before)
+  /// CHECK-DAG: <<I1:i\d+>>   IntConstant 1                       loop:none
+  /// CHECK-DAG: <<M1:i\d+>>   IntConstant -1                      loop:none
+  /// CHECK-DAG: <<I9:i\d+>>   IntConstant 9                       loop:none
+  /// CHECK-DAG: <<M9:i\d+>>   IntConstant -9                      loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add1:i\d+>> Add [<<Get1>>,<<I9>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add2:i\d+>> Add [<<Get2>>,<<M9>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add3:i\d+>> Add [<<Add1>>,<<Add2>>]             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Sub:i\d+>>  Sub [<<Add3>>,<<M1>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Shr:i\d+>>  Shr [<<Sub>>,<<I1>>]                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Shr>>]            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>>      outer_loop:none
+  private static void rounding_halving_add_signed_alt3(short[] b1, short[] b2, short[] bo) {
+    int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+    for (int i = 0; i < min_length; i++) {
+      // Computations that cancel to adding 1 also do not confuse recognition.
+      bo[i] = (short) (((b1[i] + 9) + (b2[i] - 9) - $inline$mone()) >> 1);
+    }
+  }
+
   /// CHECK-START: void Main.rounding_halving_add_unsigned(short[], short[], short[]) instruction_simplifier (before)
   /// CHECK-DAG: <<I1:i\d+>>   IntConstant 1                       loop:none
   /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535                   loop:none
@@ -366,6 +399,11 @@
       short e = (short) ((sB1[i] + sB2[i] + 1) >> 1);
       expectEquals(e, sBo[i]);
     }
+    rounding_halving_add_signed_alt3(sB1, sB2, sBo);
+    for (int i = 0; i < M; i++) {
+      short e = (short) ((sB1[i] + sB2[i] + 1) >> 1);
+      expectEquals(e, sBo[i]);
+    }
     rounding_halving_add_unsigned(sB1, sB2, sBo);
     for (int i = 0; i < M; i++) {
       short e = (short) (((sB1[i] & 0xffff) + (sB2[i] & 0xffff) + 1) >> 1);
diff --git a/test/1940-ddms-ext/check b/test/651-checker-simd-minmax/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/651-checker-simd-minmax/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/651-checker-simd-minmax/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
index b995494..8dacd5d 100644
--- a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
+++ b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
@@ -129,7 +129,7 @@
   /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
-  /// CHECK-START-{ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after)
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after)
   /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                      loop:none
   /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>]        loop:none
   /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
@@ -142,6 +142,38 @@
     }
   }
 
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMax(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<I11:i\d+>>  IntConstant -11                      loop:none
+  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 23                       loop:none
+  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
+  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
+  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
+  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
+  private static void doitMinMax(byte[] x, byte[] y) {
+    int n = Math.min(x.length, y.length);
+    for (int i = 0; i < n; i++) {
+      x[i] = (byte) Math.max(-11, Math.min(y[i], 23));
+    }
+  }
+
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMaxUnsigned(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<I11:i\d+>>  IntConstant 11                       loop:none
+  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 23                       loop:none
+  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
+  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
+  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
+  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
+  private static void doitMinMaxUnsigned(byte[] x, byte[] y) {
+    int n = Math.min(x.length, y.length);
+    for (int i = 0; i < n; i++) {
+      x[i] = (byte) Math.max(11, Math.min(y[i] & 0xff, 23));
+    }
+  }
+
   public static void main() {
     // Initialize cross-values for all possible values.
     int total = 256 * 256;
@@ -184,6 +216,18 @@
       byte expected = (byte) Math.min(y[i], 100);
       expectEquals(expected, x[i]);
     }
+    doitMinMax(x, y);
+    for (int i = 0; i < total; i++) {
+      int s = y[i];
+      byte expected = (byte) (s < -11 ? -11 : (s > 23 ? 23 : s));
+      expectEquals(expected, x[i]);
+    }
+    doitMinMaxUnsigned(x, y);
+    for (int i = 0; i < total; i++) {
+      int u = y[i] & 0xff;
+      byte expected = (byte) (u < 11 ? 11 : (u > 23 ? 23 : u));
+      expectEquals(expected, x[i]);
+    }
 
     System.out.println("ByteSimdMinMax passed");
   }
diff --git a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
index aae7891..9075d80 100644
--- a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
+++ b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
@@ -129,7 +129,7 @@
   /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
-  /// CHECK-START-{ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after)
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after)
   /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                      loop:none
   /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>]        loop:none
   /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>   outer_loop:none
@@ -142,6 +142,38 @@
     }
   }
 
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMax(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<I11:i\d+>>  IntConstant -1111                    loop:none
+  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 2323                     loop:none
+  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
+  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
+  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
+  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
+  private static void doitMinMax(short[] x, short[] y) {
+    int n = Math.min(x.length, y.length);
+    for (int i = 0; i < n; i++) {
+      x[i] = (short) Math.max(-1111, Math.min(y[i], 2323));
+    }
+  }
+
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMaxUnsigned(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<I11:i\d+>>  IntConstant 1111                     loop:none
+  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 2323                     loop:none
+  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
+  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
+  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
+  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
+  private static void doitMinMaxUnsigned(short[] x, short[] y) {
+    int n = Math.min(x.length, y.length);
+    for (int i = 0; i < n; i++) {
+      x[i] = (short) Math.max(1111, Math.min(y[i] & 0xffff, 2323));
+    }
+  }
+
   public static void main() {
     short[] interesting = {
       (short) 0x0000, (short) 0x0001, (short) 0x007f,
@@ -198,6 +230,18 @@
       short expected = (short) Math.min(y[i], 100);
       expectEquals(expected, x[i]);
     }
+    doitMinMax(x, y);
+    for (int i = 0; i < total; i++) {
+      int s = y[i];
+      short expected = (short) (s < -1111 ? -1111 : (s > 2323 ? 2323 : s));
+      expectEquals(expected, x[i]);
+    }
+    doitMinMaxUnsigned(x, y);
+    for (int i = 0; i < total; i++) {
+      int u = y[i] & 0xffff;
+      short expected = (short) (u < 1111 ? 1111 : (u > 2323 ? 2323 : u));
+      expectEquals(expected, x[i]);
+    }
 
     System.out.println("ShortSimdMinMax passed");
   }
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-byte/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-byte/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-byte/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-char/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-char/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-char/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-int/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-int/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-int/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-short/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-short/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-short/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/660-checker-simd-sad-short/src/Main.java b/test/660-checker-simd-sad-short/src/Main.java
index 8a44d9e..77c9e53 100644
--- a/test/660-checker-simd-sad-short/src/Main.java
+++ b/test/660-checker-simd-sad-short/src/Main.java
@@ -19,6 +19,10 @@
  */
 public class Main {
 
+  private static int $inline$seven() {
+    return 7;
+  }
+
   // TODO: lower precision still coming, b/64091002
 
   private static short sadShort2Short(short[] s1, short[] s2) {
@@ -153,6 +157,102 @@
     return sad;
   }
 
+  /// CHECK-START: int Main.sadShort2IntConstant1(short[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none
+  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant -7                 loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:s\d+>>   ArrayGet [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add:i\d+>>    Add [<<Get1>>,<<Cons>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Intrin:i\d+>> Abs [<<Add>>]                  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi2>>,<<Intrin>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]       loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant1(short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none
+  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant 7                  loop:none
+  /// CHECK-DAG: <<Cons8:i\d+>>  IntConstant 8                  loop:none
+  /// CHECK-DAG: <<Rep:d\d+>>    VecReplicateScalar [<<Cons>>]  loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<Cons0>>]      loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]         loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>  VecLoad [{{l\d+}},<<Phi1>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<SAD:d\d+>>    VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Rep>>] loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons8>>]       loop:<<Loop>>      outer_loop:none
+  private static int sadShort2IntConstant1(short[] s) {
+    int sad = 0;
+    for (int i = 0; i < s.length; i++) {
+      sad += Math.abs(s[i] - 7);  // s[i] + -7
+    }
+    return sad;
+  }
+
+  /// CHECK-START: int Main.sadShort2IntConstant2(short[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none
+  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant 7                  loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:s\d+>>   ArrayGet [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Sub:i\d+>>    Sub [<<Get1>>,<<Cons>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Intrin:i\d+>> Abs [<<Sub>>]                  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi2>>,<<Intrin>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]       loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant2(short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none
+  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant 7                  loop:none
+  /// CHECK-DAG: <<Cons8:i\d+>>  IntConstant 8                  loop:none
+  /// CHECK-DAG: <<Rep:d\d+>>    VecReplicateScalar [<<Cons>>]  loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<Cons0>>]      loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]         loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>  VecLoad [{{l\d+}},<<Phi1>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<SAD:d\d+>>    VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Rep>>] loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons8>>]       loop:<<Loop>>      outer_loop:none
+  private static int sadShort2IntConstant2(short[] s) {
+    int sad = 0;
+    for (int i = 0; i < s.length; i++) {
+      sad += Math.abs(s[i] - $inline$seven());  // s[i] - 7
+    }
+    return sad;
+  }
+
+  /// CHECK-START: int Main.sadShort2IntConstant3(short[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none
+  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant 7                  loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:s\d+>>   ArrayGet [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add:i\d+>>    Add [<<Get1>>,<<Cons>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Intrin:i\d+>> Abs [<<Add>>]                  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi2>>,<<Intrin>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]       loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant3(short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none
+  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant -7                 loop:none
+  /// CHECK-DAG: <<Cons8:i\d+>>  IntConstant 8                  loop:none
+  /// CHECK-DAG: <<Rep:d\d+>>    VecReplicateScalar [<<Cons>>]  loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<Cons0>>]      loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]         loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>  VecLoad [{{l\d+}},<<Phi1>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<SAD:d\d+>>    VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Rep>>] loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons8>>]       loop:<<Loop>>      outer_loop:none
+  private static int sadShort2IntConstant3(short[] s) {
+    int sad = 0;
+    for (int i = 0; i < s.length; i++) {
+      sad += Math.abs(s[i] + $inline$seven());  // hidden s[i] - (-7)
+    }
+    return sad;
+  }
+
   /// CHECK-START: long Main.sadShort2Long(short[], short[]) loop_optimization (before)
   /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none
   /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none
@@ -243,6 +343,9 @@
     expectEquals(65535, sadShort2IntAlt(s2, s1));
     expectEquals(65535, sadShort2IntAlt2(s1, s2));
     expectEquals(65535, sadShort2IntAlt2(s2, s1));
+    expectEquals(32880, sadShort2IntConstant1(s1));
+    expectEquals(32880, sadShort2IntConstant2(s1));
+    expectEquals(32866, sadShort2IntConstant3(s1));
     expectEquals(65535L, sadShort2Long(s1, s2));
     expectEquals(65535L, sadShort2Long(s2, s1));
     expectEquals(65536L, sadShort2LongAt1(s1, s2));
@@ -279,6 +382,9 @@
     expectEquals(1291788, sadShort2Int(s1, s2));
     expectEquals(1291788, sadShort2IntAlt(s1, s2));
     expectEquals(1291788, sadShort2IntAlt2(s1, s2));
+    expectEquals(823907, sadShort2IntConstant1(s1));
+    expectEquals(823907, sadShort2IntConstant2(s1));
+    expectEquals(823953, sadShort2IntConstant3(s1));
     expectEquals(1291788L, sadShort2Long(s1, s2));
     expectEquals(1291789L, sadShort2LongAt1(s1, s2));
 
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-short2/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-short2/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-short2/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-short3/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-short3/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-short3/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/661-checker-simd-reduc/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/661-checker-simd-reduc/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/661-checker-simd-reduc/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/672-checker-throw-method/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/672-checker-throw-method/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/672-checker-throw-method/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/673-checker-throw-vmethod/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/673-checker-throw-vmethod/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/673-checker-throw-vmethod/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/678-checker-simd-saturation/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/678-checker-simd-saturation/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/678-checker-simd-saturation/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java
index d123cc2..7a22ca1 100644
--- a/test/678-checker-simd-saturation/src/Main.java
+++ b/test/678-checker-simd-saturation/src/Main.java
@@ -19,6 +19,14 @@
  */
 public class Main {
 
+  static final int $inline$p15() {
+    return 15;
+  }
+
+  static final int $inline$m15() {
+    return -15;
+  }
+
   //
   // Direct min-max.
   //
@@ -230,8 +238,8 @@
   /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSByte(byte[], byte[]) loop_optimization (after)
   /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
   /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
   public static void satSubPConstSByte(byte[] a, byte[] b) {
     int n = Math.min(a.length, b.length);
     for (int i = 0; i < n; i++) {
@@ -242,8 +250,8 @@
   /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSByte(byte[], byte[]) loop_optimization (after)
   /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
   /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
   public static void satSubNConstSByte(byte[] a, byte[] b) {
     int n = Math.min(a.length, b.length);
     for (int i = 0; i < n; i++) {
@@ -282,8 +290,8 @@
   /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSShort(short[], short[]) loop_optimization (after)
   /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
   /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
   public static void satSubPConstSShort(short[] a, short[] b) {
     int n = Math.min(a.length, b.length);
     for (int i = 0; i < n; i++) {
@@ -294,8 +302,8 @@
   /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSShort(short[], short[]) loop_optimization (after)
   /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
   /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
   public static void satSubNConstSShort(short[] a, short[] b) {
     int n = Math.min(a.length, b.length);
     for (int i = 0; i < n; i++) {
@@ -304,7 +312,59 @@
   }
 
   //
-  // Alternatives.
+  // Alternatives 8-bit clipping.
+  //
+
+  /// CHECK-START-{ARM,ARM64}: void Main.usatAddConst(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
+  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
+  public static void usatAddConst(byte[] a, byte[] b) {
+    int n = Math.min(a.length, b.length);
+    for (int i = 0; i < n; i++) {
+      b[i] = (byte) Math.min((a[i] & 0xff) + $inline$p15(), 255);
+    }
+  }
+
+  /// CHECK-START-{ARM,ARM64}: void Main.usatAddConstAlt(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
+  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
+  public static void usatAddConstAlt(byte[] a, byte[] b) {
+    int n = Math.min(a.length, b.length);
+    for (int i = 0; i < n; i++) {
+      b[i] = (byte) Math.min((a[i] & 0xff) - $inline$m15(), 255);
+    }
+  }
+
+  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
+  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
+  public static void usatSubConst(byte[] a, byte[] b) {
+    int n = Math.min(a.length, b.length);
+    for (int i = 0; i < n; i++) {
+      b[i] = (byte) Math.max((a[i] & 0xff) - $inline$p15(), 0);
+    }
+  }
+
+  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
+  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
+  public static void usatSubConstAlt(byte[] a, byte[] b) {
+    int n = Math.min(a.length, b.length);
+    for (int i = 0; i < n; i++) {
+      b[i] = (byte) Math.max((a[i] & 0xff) + $inline$m15(), 0);
+    }
+  }
+
+  //
+  // Alternatives 16-bit clipping.
   //
 
   /// CHECK-START: void Main.satAlt1(short[], short[], short[]) loop_optimization (before)
@@ -337,7 +397,22 @@
     }
   }
 
-  // TODO: recognize the more common if-else too.
+  /// CHECK-START: void Main.satAlt2(short[], short[], short[]) loop_optimization (before)
+  /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -32768                   loop:none
+  /// CHECK-DAG: <<Clp2:i\d+>> IntConstant  32767                   loop:none
+  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add:i\d+>>  Add [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Add>>,<<Clp1>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Max>>,<<Clp2>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Min>>]             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-{ARM,ARM64}: void Main.satAlt2(short[], short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
   public static void satAlt2(short[] a, short[] b, short[] c) {
     int n = Math.min(a.length, Math.min(b.length, c.length));
     for (int i = 0; i < n; i++) {
@@ -351,7 +426,11 @@
     }
   }
 
-  // TODO: recognize conditional too.
+  /// CHECK-START-{ARM,ARM64}: void Main.satAlt3(short[], short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
   public static void satAlt3(short[] a, short[] b, short[] c) {
     int n = Math.min(a.length, Math.min(b.length, c.length));
     for (int i = 0; i < n; i++) {
@@ -442,6 +521,34 @@
     }
   }
 
+  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
+  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
+  public static void usatSubConst(short[] a, short[] b) {
+    int n = Math.min(a.length, b.length);
+    for (int i = 0; i < n; i++) {
+      int t = a[i] & 0xffff;
+      int s = t - $inline$p15();
+      b[i] = (short)(s > 0 ? s : 0);
+    }
+  }
+
+  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
+  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
+  public static void usatSubConstAlt(short[] a, short[] b) {
+    int n = Math.min(a.length, b.length);
+    for (int i = 0; i < n; i++) {
+      int t = a[i] & 0xffff;
+      int s = t + $inline$m15();
+      b[i] = (short)(s > 0 ? s : 0);
+    }
+  }
+
   //
   // Test drivers.
   //
@@ -503,6 +610,27 @@
       byte e = (byte) Math.max(-15 - b1[i], -128);
       expectEquals(e, out[i]);
     }
+    // Alternatives.
+    usatAddConst(b1, out);
+    for (int i = 0; i < m; i++) {
+      byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255);
+      expectEquals(e, out[i]);
+    }
+    usatAddConstAlt(b1, out);
+    for (int i = 0; i < m; i++) {
+      byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255);
+      expectEquals(e, out[i]);
+    }
+    usatSubConst(b1, out);
+    for (int i = 0; i < m; i++) {
+      byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0);
+      expectEquals(e, out[i]);
+    }
+    usatSubConstAlt(b1, out);
+    for (int i = 0; i < m; i++) {
+      byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0);
+      expectEquals(e, out[i]);
+    }
   }
 
   private static void test16Bit() {
@@ -630,6 +758,16 @@
       short e = (short) Math.max(Math.min(s1[i] + 15, 32767), -32752);
       expectEquals(e, out[i]);
     }
+    usatSubConst(s1, out);
+    for (int i = 0; i < m; i++) {
+      short e = (short) Math.max((s1[i] & 0xffff) - 15, 0);
+      expectEquals(e, out[i]);
+    }
+    usatSubConstAlt(s1, out);
+    for (int i = 0; i < m; i++) {
+      short e = (short) Math.max((s1[i] & 0xffff) - 15, 0);
+      expectEquals(e, out[i]);
+    }
   }
 
   public static void main(String[] args) {
diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java
index 38085bb..e330a53 100644
--- a/test/679-checker-minmax/src/Main.java
+++ b/test/679-checker-minmax/src/Main.java
@@ -19,6 +19,86 @@
  */
 public class Main {
 
+  //
+  // Direct intrinsics.
+  //
+
+  /// CHECK-START: int Main.minI(int) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+  /// CHECK-DAG: <<Min:i\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMinIntInt
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: int Main.minI(int) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+  /// CHECK-DAG: <<Min:i\d+>> Min [<<Par>>,<<Con>>]
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: int Main.minI(int) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  public static int minI(int a) {
+    return Math.min(a, 20);
+  }
+
+  /// CHECK-START: long Main.minL(long) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+  /// CHECK-DAG: <<Min:j\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMinLongLong
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: long Main.minL(long) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+  /// CHECK-DAG: <<Min:j\d+>> Min [<<Par>>,<<Con>>]
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: long Main.minL(long) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  public static long minL(long a) {
+    return Math.min(a, 20L);
+  }
+
+  /// CHECK-START: int Main.maxI(int) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+  /// CHECK-DAG: <<Max:i\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMaxIntInt
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: int Main.maxI(int) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+  /// CHECK-DAG: <<Max:i\d+>> Max [<<Par>>,<<Con>>]
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: int Main.maxI(int) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  public static int maxI(int a) {
+    return Math.max(a, 20);
+  }
+
+  /// CHECK-START: long Main.maxL(long) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+  /// CHECK-DAG: <<Max:j\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMaxLongLong
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: long Main.maxL(long) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+  /// CHECK-DAG: <<Max:j\d+>> Max [<<Par>>,<<Con>>]
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: long Main.maxL(long) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  public static long maxL(long a) {
+    return Math.max(a, 20L);
+  }
+
+  //
+  // Different types.
+  //
+
   /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (before)
   /// CHECK-DAG: <<Cnd:z\d+>> GreaterThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
@@ -229,7 +309,124 @@
     return a >= b ? a : b;
   }
 
+
+  //
+  // Complications.
+  //
+
+  // TODO: coming soon, under discussion
+  public static int min0(int[] a, int[] b) {
+    // Repeat of array references needs finding the common subexpressions
+    // prior to doing the select and min/max recognition.
+    return a[0] <= b[0] ? a[0] : b[0];
+  }
+
+  // TODO: coming soon, under discussion
+  public static int max0(int[] a, int[] b) {
+    // Repeat of array references needs finding the common subexpressions
+    // prior to doing the select and min/max recognition.
+    return a[0] >= b[0] ? a[0] : b[0];
+  }
+
+  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
+  /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+  /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+  /// CHECK-DAG: <<Cnd1:z\d+>> LessThanOrEqual [<<Par>>,<<P100>>]
+  /// CHECK-DAG: <<Sel1:i\d+>> Select [<<P100>>,<<Par>>,<<Cnd1>>]
+  /// CHECK-DAG: <<Cnd2:z\d+>> GreaterThanOrEqual [<<Sel1>>,<<M100>>]
+  /// CHECK-DAG: <<Sel2:i\d+>> Select [<<M100>>,<<Sel1>>,<<Cnd2>>]
+  /// CHECK-DAG:               Return [<<Sel2>>]
+  //
+  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
+  /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+  /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Par>>,<<P100>>]
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<M100>>]
+  /// CHECK-DAG:               Return [<<Max>>]
+  //
+  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:               Select
+  public static int minmax1(int x) {
+    // Simple if-if gives clean select sequence.
+    if (x > 100) {
+      x = 100;
+    }
+    if (x < -100) {
+      x = -100;
+    }
+    return x;
+  }
+
+  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
+  /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+  /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+  /// CHECK-DAG: <<Cnd1:z\d+>> LessThanOrEqual [<<Par>>,<<P100>>]
+  /// CHECK-DAG: <<Cnd2:z\d+>> GreaterThanOrEqual [<<Par>>,<<M100>>]
+  /// CHECK-DAG: <<Sel1:i\d+>> Select [<<M100>>,<<Par>>,<<Cnd2>>]
+  /// CHECK-DAG: <<Sel2:i\d+>> Select [<<P100>>,<<Sel1>>,<<Cnd1>>]
+  /// CHECK-DAG:               Return [<<Sel2>>]
+  //
+  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
+  /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+  /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Par>>,<<M100>>]
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Max>>,<<P100>>]
+  /// CHECK-DAG:               Return [<<Min>>]
+  //
+  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:               Select
+  public static int minmax2(int x) {
+    // Simple if-else requires inspecting bounds of resulting selects.
+    if (x > 100) {
+      x = 100;
+    } else if (x < -100) {
+      x = -100;
+    }
+    return x;
+  }
+
+  /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
+  /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+  /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Par>>,<<M100>>]
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Max>>,<<P100>>]
+  /// CHECK-DAG:               Return [<<Min>>]
+  //
+  /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:               Select
+  public static int minmax3(int x) {
+    return (x > 100) ? 100 : ((x < -100) ? -100 : x);
+  }
+
+  /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
+  /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+  /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Par>>,<<P100>>]
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<M100>>]
+  /// CHECK-DAG:               Return [<<Max>>]
+  //
+  /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:               Select
+  public static int minmax4(int x) {
+    return (x < -100) ? -100 : ((x > 100) ? 100 : x);
+  }
+
   public static void main(String[] args) {
+    // Types.
+    expectEquals(10, minI(10));
+    expectEquals(20, minI(25));
+    expectEquals(10L, minL(10L));
+    expectEquals(20L, minL(25L));
+    expectEquals(20, maxI(10));
+    expectEquals(25, maxI(25));
+    expectEquals(20L, maxL(10L));
+    expectEquals(25L, maxL(25L));
     expectEquals(10, min1(10, 20));
     expectEquals(10, min2(10, 20));
     expectEquals(10, min3(10, 20));
@@ -244,6 +441,23 @@
     expectEquals(20, max5((short) 10, (short) 20));
     expectEquals(20, max6((byte) 10, (byte) 20));
     expectEquals(20L, max7(10L, 20L));
+    // Complications.
+    int[] a = { 10 };
+    int[] b = { 20 };
+    expectEquals(10, min0(a, b));
+    expectEquals(20, max0(a, b));
+    expectEquals(-100, minmax1(-200));
+    expectEquals(10, minmax1(10));
+    expectEquals(100, minmax1(200));
+    expectEquals(-100, minmax2(-200));
+    expectEquals(10, minmax2(10));
+    expectEquals(100, minmax2(200));
+    expectEquals(-100, minmax3(-200));
+    expectEquals(10, minmax3(10));
+    expectEquals(100, minmax3(200));
+    expectEquals(-100, minmax4(-200));
+    expectEquals(10, minmax4(10));
+    expectEquals(100, minmax4(200));
     System.out.println("passed");
   }
 
diff --git a/test/680-checker-deopt-dex-pc-0/src/Main.java b/test/680-checker-deopt-dex-pc-0/src/Main.java
index d5a6a90..64a3cb3 100644
--- a/test/680-checker-deopt-dex-pc-0/src/Main.java
+++ b/test/680-checker-deopt-dex-pc-0/src/Main.java
@@ -31,15 +31,12 @@
         System.loadLibrary(args[0]);
         if (hasJit()) {
             byte[] array = { 0, 1, 2, 3 };
-            while (!hasJitCompiledEntrypoint(Main.class, "$noinline$getInt")) {
-                for (int i = 0; i < 10000; ++i) {
-                    if ($noinline$getInt(array, 0) != 0x03020100) {
-                        throw new Error();
-                    }
-                }
-                try {
-                    Thread.sleep(200);
-                } catch (InterruptedException ignored) {}
+            ensureJitCompiled(Main.class, "$noinline$getInt");
+            if (!hasJitCompiledEntrypoint(Main.class, "$noinline$getInt")) {
+                throw new Error("Unexpected entrypoint!");
+            }
+            if ($noinline$getInt(array, 0) != 0x03020100) {
+                throw new Error();
             }
             try {
                 // The HDeoptimize at dex pc 0 was previously handled poorly as the dex pc 0
@@ -56,4 +53,5 @@
 
     public static native boolean hasJit();
     public native static boolean hasJitCompiledEntrypoint(Class<?> cls, String methodName);
+    public native static void ensureJitCompiled(Class<?> cls, String methodName);
 }
diff --git a/test/681-checker-abs/src/Main.java b/test/681-checker-abs/src/Main.java
index 8064b1d..d1ba7c6 100644
--- a/test/681-checker-abs/src/Main.java
+++ b/test/681-checker-abs/src/Main.java
@@ -19,6 +19,38 @@
  */
 public class Main {
 
+  /// CHECK-START: int Main.absI(int) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+  /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<Par>>] intrinsic:MathAbsInt
+  /// CHECK-DAG:              Return [<<Abs>>]
+  //
+  /// CHECK-START: int Main.absI(int) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+  /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
+  /// CHECK-DAG:              Return [<<Abs>>]
+  //
+  /// CHECK-START: int Main.absI(int) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  public static int absI(int a) {
+    return Math.abs(a);
+  }
+
+  /// CHECK-START: long Main.absL(long) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+  /// CHECK-DAG: <<Abs:j\d+>> InvokeStaticOrDirect [<<Par>>] intrinsic:MathAbsLong
+  /// CHECK-DAG:              Return [<<Abs>>]
+  //
+  /// CHECK-START: long Main.absL(long) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+  /// CHECK-DAG: <<Abs:j\d+>> Abs [<<Par>>]
+  /// CHECK-DAG:              Return [<<Abs>>]
+  //
+  /// CHECK-START: long Main.absL(long) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  public static long absL(long a) {
+    return Math.abs(a);
+  }
+
   /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (before)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0
@@ -152,7 +184,74 @@
     return a >= 0 ? a : -a;
   }
 
+  //
+  // Nop zero extension.
+  //
+
+  /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:b\d+>> ParameterValue
+  /// CHECK-DAG: <<Msk:i\d+>> IntConstant 255
+  /// CHECK-DAG: <<And:i\d+>> [<<Par>>,<<Msk>>]
+  /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<And>>] intrinsic:MathAbsInt
+  /// CHECK-DAG:              Return [<<Abs>>]
+  //
+  /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:b\d+>> ParameterValue
+  /// CHECK-DAG: <<Cnv:a\d+>> TypeConversion [<<Par>>]
+  /// CHECK-DAG:              Return [<<Cnv>>]
+  //
+  /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  /// CHECK-NOT:              Abs
+  public static int zabs1(byte a) {
+    return Math.abs(a & 0xff);
+  }
+
+  /// CHECK-START: int Main.zabs2(short) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:s\d+>> ParameterValue
+  /// CHECK-DAG: <<Msk:i\d+>> IntConstant 65535
+  /// CHECK-DAG: <<And:i\d+>> [<<Msk>>,<<Par>>]
+  /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<And>>] intrinsic:MathAbsInt
+  /// CHECK-DAG:              Return [<<Abs>>]
+  //
+  /// CHECK-START: int Main.zabs2(short) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:s\d+>> ParameterValue
+  /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Par>>]
+  /// CHECK-DAG:              Return [<<Cnv>>]
+  //
+  /// CHECK-START: int Main.zabs2(short) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  /// CHECK-NOT:              Abs
+  public static int zabs2(short a) {
+    return Math.abs(a & 0xffff);
+  }
+
+  /// CHECK-START: int Main.zabs3(char) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:c\d+>> ParameterValue
+  /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<Par>>] intrinsic:MathAbsInt
+  /// CHECK-DAG:              Return [<<Abs>>]
+  //
+  /// CHECK-START: int Main.zabs3(char) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:c\d+>> ParameterValue
+  /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
+  /// CHECK-DAG:              Return [<<Abs>>]
+  //
+  /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Par:c\d+>> ParameterValue
+  /// CHECK-DAG:              Return [<<Par>>]
+  //
+  /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  /// CHECK-NOT:              Abs
+  public static int zabs3(char a) {
+    return Math.abs(a);
+  }
+
   public static void main(String[] args) {
+    expectEquals(10, absI(-10));
+    expectEquals(20, absI(20));
+    expectEquals(10L, absL(-10L));
+    expectEquals(20L, absL(20L));
     expectEquals(10, abs1(-10));
     expectEquals(20, abs1(20));
     expectEquals(10, abs2(-10));
@@ -167,6 +266,12 @@
     expectEquals(20, abs6((byte) 20));
     expectEquals(10L, abs7(-10L));
     expectEquals(20L, abs7(20L));
+    expectEquals(1, zabs1((byte) 1));
+    expectEquals(0xff, zabs1((byte) -1));
+    expectEquals(1, zabs2((short) 1));
+    expectEquals(0xffff, zabs2((short) -1));
+    expectEquals(1, zabs3((char) 1));
+    expectEquals(0xffff, zabs3((char) -1));
     System.out.println("passed");
   }
 
diff --git a/test/1940-ddms-ext/check b/test/704-multiply-accumulate/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/704-multiply-accumulate/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/704-multiply-accumulate/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/706-checker-scheduler/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/706-checker-scheduler/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/706-checker-scheduler/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 2018 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
+#      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,
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
+# See b/65168732
+export USE_D8=false
 
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/712-varhandle-invocations/build b/test/712-varhandle-invocations/build
index 253765b..6d4429f 100755
--- a/test/712-varhandle-invocations/build
+++ b/test/712-varhandle-invocations/build
@@ -35,5 +35,8 @@
 # Desugar is not happy with our Java 9 byte code, it shouldn't be necessary here anyway.
 export USE_DESUGAR=false
 
+# See b/65168732
+export USE_D8=false
+
 # Invoke default build with increased heap size for dx
 ./default-build "$@" --experimental var-handles --dx-vm-option -JXmx384m
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 6633958..f8bebdd 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -21,17 +21,12 @@
 TEST_ART_RUN_TEST_DEPENDENCIES := \
   $(HOST_OUT_EXECUTABLES)/dx \
   $(HOST_OUT_EXECUTABLES)/d8 \
+  $(HOST_OUT_EXECUTABLES)/d8-compat-dx \
   $(HOST_OUT_EXECUTABLES)/hiddenapi \
   $(HOST_OUT_EXECUTABLES)/jasmin \
   $(HOST_OUT_EXECUTABLES)/smali \
   $(HOST_OUT_JAVA_LIBRARIES)/desugar.jar
 
-# Add d8 dependency, if enabled.
-ifeq ($(USE_D8),true)
-TEST_ART_RUN_TEST_DEPENDENCIES += \
-  $(HOST_OUT_EXECUTABLES)/d8-compat-dx
-endif
-
 # We need dex2oat and dalvikvm on the target as well as the core images (all images as we sync
 # only once).
 TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS)
diff --git a/test/etc/default-build b/test/etc/default-build
index 9de7294..dd55602 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -317,7 +317,7 @@
   fi
 
   local dexer="${DX}"
-  if [ ${USE_D8} = "true" ]; then
+  if [[ "${USE_D8}" != "false" ]]; then
     dexer="${ANDROID_HOST_OUT}/bin/d8-compat-dx"
   fi
 
diff --git a/test/run-test b/test/run-test
index 5b43b52..5f85b08 100755
--- a/test/run-test
+++ b/test/run-test
@@ -45,7 +45,7 @@
 export RUN="${progdir}/etc/run-test-jar"
 export DEX_LOCATION=/data/run-test/${test_dir}
 export NEED_DEX="true"
-export USE_D8="false"
+export USE_D8="true"
 export USE_JACK="false"
 export USE_DESUGAR="true"
 export SMALI_ARGS=""
@@ -365,9 +365,6 @@
     elif [ "x$1" = "x--build-only" ]; then
         build_only="yes"
         shift
-    elif [ "x$1" = "x--build-with-d8" ]; then
-        USE_D8="true"
-        shift
     elif [ "x$1" = "x--build-with-javac-dx" ]; then
         USE_JACK="false"
         shift
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 5394991..7564f5a 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -71,9 +71,6 @@
 # Compiling with jack? Possible values in (True, False, 'default')
 ANDROID_COMPILE_WITH_JACK = _get_build_var_boolean('ANDROID_COMPILE_WITH_JACK', 'default')
 
-# Follow the build system's D8 usage.
-USE_D8_BY_DEFAULT = _get_build_var_boolean('USE_D8_BY_DEFAULT', False)
-
 # Directory used for temporary test files on the host.
 ART_HOST_TEST_DIR = tempfile.mkdtemp(prefix = 'test-art-')
 
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 734a600..88b509d 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -174,56 +174,37 @@
 
   global _user_input_variants
   global run_all_configs
+  # These are the default variant-options we will use if nothing in the group is specified.
+  default_variants = {
+      'target': {'host', 'target'},
+      'pictest': {'npictest'},
+      'prebuild': {'prebuild'},
+      'cdex_level': {'cdex-fast'},
+      'jvmti': { 'no-jvmti'},
+      'compiler': {'optimizing',
+                   'jit',
+                   'interpreter',
+                   'interp-ac',
+                   'speed-profile'},
+      'relocate': {'no-relocate'},
+      'trace': {'ntrace'},
+      'gc': {'cms'},
+      'jni': {'checkjni'},
+      'image': {'picimage'},
+      'pictest': {'pictest'},
+      'debuggable': {'ndebuggable'},
+      'run': {'debug'},
+      # address_sizes_target depends on the target so it is dealt with below.
+  }
+  # We want to pull these early since the full VARIANT_TYPE_DICT has a few additional ones we don't
+  # want to pick up if we pass --all.
+  default_variants_keys = default_variants.keys()
   if run_all_configs:
-    target_types = _user_input_variants['target']
-    _user_input_variants = VARIANT_TYPE_DICT
-    _user_input_variants['target'] = target_types
+    default_variants = VARIANT_TYPE_DICT
 
-  if not _user_input_variants['target']:
-    _user_input_variants['target'].add('host')
-    _user_input_variants['target'].add('target')
-
-  if not _user_input_variants['prebuild']: # Default
-    _user_input_variants['prebuild'].add('prebuild')
-
-  if not _user_input_variants['cdex_level']: # Default
-    _user_input_variants['cdex_level'].add('cdex-fast')
-
-  # By default only run without jvmti
-  if not _user_input_variants['jvmti']:
-    _user_input_variants['jvmti'].add('no-jvmti')
-
-  # By default we run all 'compiler' variants.
-  if not _user_input_variants['compiler'] and _user_input_variants['target'] != 'jvm':
-    _user_input_variants['compiler'].add('optimizing')
-    _user_input_variants['compiler'].add('jit')
-    _user_input_variants['compiler'].add('interpreter')
-    _user_input_variants['compiler'].add('interp-ac')
-    _user_input_variants['compiler'].add('speed-profile')
-
-  if not _user_input_variants['relocate']: # Default
-    _user_input_variants['relocate'].add('no-relocate')
-
-  if not _user_input_variants['trace']: # Default
-    _user_input_variants['trace'].add('ntrace')
-
-  if not _user_input_variants['gc']: # Default
-    _user_input_variants['gc'].add('cms')
-
-  if not _user_input_variants['jni']: # Default
-    _user_input_variants['jni'].add('checkjni')
-
-  if not _user_input_variants['image']: # Default
-    _user_input_variants['image'].add('picimage')
-
-  if not _user_input_variants['pictest']: # Default
-    _user_input_variants['pictest'].add('npictest')
-
-  if not _user_input_variants['debuggable']: # Default
-    _user_input_variants['debuggable'].add('ndebuggable')
-
-  if not _user_input_variants['run']: # Default
-    _user_input_variants['run'].add('debug')
+  for key in default_variants_keys:
+    if not _user_input_variants[key]:
+      _user_input_variants[key] = default_variants[key]
 
   _user_input_variants['address_sizes_target'] = collections.defaultdict(set)
   if not _user_input_variants['address_sizes']:
@@ -504,9 +485,6 @@
       elif env.ANDROID_COMPILE_WITH_JACK == False:
         options_test += ' --build-with-javac-dx'
 
-      if env.USE_D8_BY_DEFAULT == True:
-        options_test += ' --build-with-d8'
-
       # TODO(http://36039166): This is a temporary solution to
       # fix build breakages.
       options_test = (' --output-path %s') % (
diff --git a/tools/build/var_list b/tools/build/var_list
index 3727741..adcb066 100644
--- a/tools/build/var_list
+++ b/tools/build/var_list
@@ -34,5 +34,4 @@
 HOST_OUT_EXECUTABLES
 ANDROID_JAVA_TOOLCHAIN
 ANDROID_COMPILE_WITH_JACK
-USE_D8_BY_DEFAULT
 
diff --git a/tools/libcore_network_failures.txt b/tools/libcore_network_failures.txt
new file mode 100644
index 0000000..e7e31db
--- /dev/null
+++ b/tools/libcore_network_failures.txt
@@ -0,0 +1,92 @@
+/*
+ * This file contains extra expectations for ART's buildbot regarding network tests.
+ * The script that uses this file is art/tools/run-libcore-tests.sh.
+ */
+
+[
+{
+  description: "Ignore failure of network-related tests on new devices running Android O",
+  result: EXEC_FAILED,
+  bug: 74725685,
+  modes: [device],
+  names: ["libcore.libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet",
+          "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithFtpURLConnection",
+          "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithHttpURLConnection",
+          "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarFtpURLConnection",
+          "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarHttpURLConnection",
+          "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithLoggingSocketHandler",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_40555",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_File",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_FileLjava_lang_String",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_InputStream",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_InputStreamLjava_lang_String",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_lang_Readable",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_lang_String",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_channels_ReadableByteChannel",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_channels_ReadableByteChannelLjava_lang_String",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_Path",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_PathLjava_lang_String",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_PathLjava_lang_String_Exception",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_Path_Exception",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_close",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_delimiter",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_findInLine_LPattern",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_findInLine_LString",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_findInLine_LString_NPEs",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_findWithinHorizon_LPatternI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNext",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigDecimal",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigInteger",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigIntegerI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigIntegerI_cache",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBoolean",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextByte",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextByteI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextByteI_cache",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextDouble",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextFloat",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextInt",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextIntI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextIntI_cache",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLPattern",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLString",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLine",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLine_sequence",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLong",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLongI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLongI_cache",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextShort",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextShortI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextShortI_cache",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_ioException",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_locale",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_match",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_next",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextBigDecimal",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextBigInteger",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextBigIntegerI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextBoolean",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextByte",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextByteI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextDouble",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextFloat",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextInt",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextIntI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextLPattern",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextLString",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextLine",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextLong",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextLongI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextShort",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_nextShortI",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_radix",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_remove",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_skip_LPattern",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_skip_LString",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_toString",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_useDelimiter_LPattern",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_useDelimiter_String",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_useLocale_LLocale",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_useRadix_I"]
+}
+]
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index de07a47..21ddcbc 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -19,6 +19,16 @@
   exit 1
 fi
 
+# Prevent JDWP tests from running on the following devices running
+# Android O (they are failing because of a network-related issue), as
+# a workaround for b/74725685:
+# - FA7BN1A04406 (walleye device testing configuration aosp-poison/volantis-armv7-poison-debug)
+# - FA7BN1A04412 (walleye device testing configuration aosp-poison/volantis-armv8-poison-ndebug)
+# - FA7BN1A04433 (walleye device testing configuration aosp-poison/volantis-armv8-poison-debug)
+case "$ANDROID_SERIAL" in
+  (FA7BN1A04406|FA7BN1A04412|FA7BN1A04433) exit 0;;
+esac
+
 source build/envsetup.sh >&/dev/null # for get_build_var, setpaths
 setpaths # include platform prebuilt java, javac, etc in $PATH.
 
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 2b7c624..7f0383d 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -161,6 +161,16 @@
   fi
 fi
 
+# Disable network-related libcore tests that are failing on the following
+# devices running Android O, as a workaround for b/74725685:
+# - FA7BN1A04406 (walleye device testing configuration aosp-poison/volantis-armv7-poison-debug)
+# - FA7BN1A04412 (walleye device testing configuration aosp-poison/volantis-armv8-poison-ndebug)
+# - FA7BN1A04433 (walleye device testing configuration aosp-poison/volantis-armv8-poison-debug)
+case "$ANDROID_SERIAL" in
+  (FA7BN1A04406|FA7BN1A04412|FA7BN1A04433)
+    expectations="$expectations --expectations art/tools/libcore_network_failures.txt";;
+esac
+
 # Run the tests using vogar.
 echo "Running tests for the following test packages:"
 echo ${working_packages[@]} | tr " " "\n"
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 9373c69..5ce7f52 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -57,14 +57,19 @@
 adb shell ifconfig lo up
 adb shell ifconfig
 
-# When netd is running, some libcore and JDWP tests fail with this
-# exception (b/74725685):
+# Ensure netd is running, as otherwise the logcat would be spammed
+# with the following messages on devices running Android O:
 #
-#   android.system.ErrnoException: connect failed: EBADMSG (Not a data message)
+#   E NetdConnector: Communications error: java.io.IOException: No such file or directory
+#   E mDnsConnector: Communications error: java.io.IOException: No such file or directory
 #
-# Turn it off to make these tests pass.
-echo -e "${green}Turning off netd${nc}"
-adb shell stop netd
+# Netd was initially disabled as an attempt to solve issues with
+# network-related libcore and JDWP tests failing on devices running
+# Android O (MR1) (see b/74725685). These tests are currently
+# disabled. When a better solution has been found, we should remove
+# the following lines.
+echo -e "${green}Turning on netd${nc}"
+adb shell start netd
 adb shell getprop init.svc.netd
 
 echo -e "${green}List properties${nc}"
diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp
index ff181c8..570960c 100644
--- a/tools/veridex/Android.bp
+++ b/tools/veridex/Android.bp
@@ -12,11 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-art_cc_binary {
+cc_binary {
     name: "veridex",
     host_supported: true,
     srcs: [
+        "flow_analysis.cc",
         "hidden_api.cc",
+        "hidden_api_finder.cc",
+        "precise_hidden_api_finder.cc",
         "resolver.cc",
         "veridex.cc",
     ],
diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk
new file mode 100644
index 0000000..4183054
--- /dev/null
+++ b/tools/veridex/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2018 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex
+$(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
+$(system_stub_dex): $(TOPDIR)prebuilts/sdk/system_current/android.jar | $(ZIP2ZIP) $(DX)
+	$(transform-classes-d8.jar-to-dex)
+
+
+oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex
+$(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
+$(oahl_stub_dex): $(TOPDIR)prebuilts/sdk/org.apache.http.legacy/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX)
+	$(transform-classes-d8.jar-to-dex)
+
+.PHONY: appcompat
+
+appcompat: $(system_stub_dex) $(oahl_stub_dex) $(HOST_OUT_EXECUTABLES)/veridex \
+  ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-light-greylist.txt \
+  ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-dark-greylist.txt \
+  ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-blacklist.txt
diff --git a/tools/veridex/README.md b/tools/veridex/README.md
new file mode 100644
index 0000000..f85a51b
--- /dev/null
+++ b/tools/veridex/README.md
@@ -0,0 +1,14 @@
+appcompat.sh
+============
+
+Given an APK, finds API uses that fall into the blacklist/greylists APIs.
+
+NOTE: appcompat.sh is still under development. It can report
+API uses that do not execute at runtime, and reflection uses
+that do not exist. It can also miss on reflection uses.
+
+To build it:
+> make appcompat
+
+To run it:
+> ./art/tools/veridex/appcompat.sh --dex-file=test.apk
diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh
new file mode 100755
index 0000000..31a8654
--- /dev/null
+++ b/tools/veridex/appcompat.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 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.
+
+# We want to be at the root for simplifying the "out" detection
+# logic.
+if [ ! -d art ]; then
+  echo "Script needs to be run at the root of the android tree."
+  exit 1
+fi
+
+# Logic for setting out_dir from build/make/core/envsetup.mk:
+if [[ -z $OUT_DIR ]]; then
+  if [[ -z $OUT_DIR_COMMON_BASE ]]; then
+    OUT=out
+  else
+    OUT=${OUT_DIR_COMMON_BASE}/${PWD##*/}
+  fi
+else
+  OUT=${OUT_DIR}
+fi
+
+PACKAGING=${OUT}/target/common/obj/PACKAGING
+
+if [ -z "$ANDROID_HOST_OUT" ] ; then
+  ANDROID_HOST_OUT=${OUT}/host/linux-x86
+fi
+
+echo "NOTE: appcompat.sh is still under development. It can report"
+echo "API uses that do not execute at runtime, and reflection uses"
+echo "that do not exist. It can also miss on reflection uses."
+
+
+${ANDROID_HOST_OUT}/bin/veridex \
+    --core-stubs=${PACKAGING}/core_dex_intermediates/classes.dex:${PACKAGING}/oahl_dex_intermediates/classes.dex \
+    --blacklist=${PACKAGING}/hiddenapi-blacklist.txt \
+    --light-greylist=${PACKAGING}/hiddenapi-light-greylist.txt \
+    --dark-greylist=${PACKAGING}/hiddenapi-dark-greylist.txt \
+    $@
diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc
new file mode 100644
index 0000000..abd0b9b
--- /dev/null
+++ b/tools/veridex/flow_analysis.cc
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "flow_analysis.h"
+
+#include "dex/bytecode_utils.h"
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_file_exception_helpers.h"
+#include "resolver.h"
+#include "veridex.h"
+
+namespace art {
+
+
+void VeriFlowAnalysis::SetAsBranchTarget(uint32_t dex_pc) {
+  if (dex_registers_[dex_pc] == nullptr) {
+    dex_registers_[dex_pc].reset(
+        new std::vector<RegisterValue>(code_item_accessor_.RegistersSize()));
+  }
+}
+
+bool VeriFlowAnalysis::IsBranchTarget(uint32_t dex_pc) {
+  return dex_registers_[dex_pc] != nullptr;
+}
+
+bool VeriFlowAnalysis::MergeRegisterValues(uint32_t dex_pc) {
+  // TODO: Do the merging. Right now, just return that we should continue
+  // the iteration if the instruction has not been visited.
+  return !instruction_infos_[dex_pc].has_been_visited;
+}
+
+void VeriFlowAnalysis::SetVisited(uint32_t dex_pc) {
+  instruction_infos_[dex_pc].has_been_visited = true;
+}
+
+void VeriFlowAnalysis::FindBranches() {
+  SetAsBranchTarget(0);
+
+  if (code_item_accessor_.TriesSize() != 0) {
+    // TODO: We need to mark the range of dex pcs as flowing in the handlers.
+    /*
+    for (const DexFile::TryItem& try_item : code_item_accessor_.TryItems()) {
+      uint32_t dex_pc_start = try_item.start_addr_;
+      uint32_t dex_pc_end = dex_pc_start + try_item.insn_count_;
+    }
+    */
+
+    // Create branch targets for exception handlers.
+    const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData();
+    uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+    for (uint32_t idx = 0; idx < handlers_size; ++idx) {
+      CatchHandlerIterator iterator(handlers_ptr);
+      for (; iterator.HasNext(); iterator.Next()) {
+        SetAsBranchTarget(iterator.GetHandlerAddress());
+      }
+      handlers_ptr = iterator.EndDataPointer();
+    }
+  }
+
+  // Iterate over all instructions and find branching instructions.
+  for (const DexInstructionPcPair& pair : code_item_accessor_) {
+    const uint32_t dex_pc = pair.DexPc();
+    const Instruction& instruction = pair.Inst();
+
+    if (instruction.IsBranch()) {
+      SetAsBranchTarget(dex_pc + instruction.GetTargetOffset());
+    } else if (instruction.IsSwitch()) {
+      DexSwitchTable table(instruction, dex_pc);
+      for (DexSwitchTableIterator s_it(table); !s_it.Done(); s_it.Advance()) {
+        SetAsBranchTarget(dex_pc + s_it.CurrentTargetOffset());
+        if (table.ShouldBuildDecisionTree() && !s_it.IsLast()) {
+          SetAsBranchTarget(s_it.GetDexPcForCurrentIndex());
+        }
+      }
+    }
+  }
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register,
+                                      RegisterSource kind,
+                                      VeriClass* cls,
+                                      uint32_t source_id) {
+  current_registers_[dex_register] = RegisterValue(
+      kind, DexFileReference(&resolver_->GetDexFile(), source_id), cls);
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const RegisterValue& value) {
+  current_registers_[dex_register] = value;
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const VeriClass* cls) {
+  current_registers_[dex_register] =
+      RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls);
+}
+
+const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) {
+  return current_registers_[dex_register];
+}
+
+RegisterValue VeriFlowAnalysis::GetReturnType(uint32_t method_index) {
+  const DexFile& dex_file = resolver_->GetDexFile();
+  const DexFile::MethodId& method_id = dex_file.GetMethodId(method_index);
+  const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id);
+  VeriClass* cls = resolver_->GetVeriClass(proto_id.return_type_idx_);
+  return RegisterValue(RegisterSource::kMethod, DexFileReference(&dex_file, method_index), cls);
+}
+
+RegisterValue VeriFlowAnalysis::GetFieldType(uint32_t field_index) {
+  const DexFile& dex_file = resolver_->GetDexFile();
+  const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+  VeriClass* cls = resolver_->GetVeriClass(field_id.type_idx_);
+  return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls);
+}
+
+void VeriFlowAnalysis::AnalyzeCode() {
+  std::vector<uint32_t> work_list;
+  work_list.push_back(0);
+  // Iterate over the code.
+  // When visiting unconditional branches (goto), move to that instruction.
+  // When visiting conditional branches, move to one destination, and put the other
+  // in the worklist.
+  while (!work_list.empty()) {
+    uint32_t dex_pc = work_list.back();
+    work_list.pop_back();
+    CHECK(IsBranchTarget(dex_pc));
+    current_registers_ = *dex_registers_[dex_pc].get();
+    while (true) {
+      const uint16_t* insns = code_item_accessor_.Insns() + dex_pc;
+      const Instruction& inst = *Instruction::At(insns);
+      ProcessDexInstruction(inst);
+      SetVisited(dex_pc);
+
+      int opcode_flags = Instruction::FlagsOf(inst.Opcode());
+      if ((opcode_flags & Instruction::kContinue) != 0) {
+        if ((opcode_flags & Instruction::kBranch) != 0) {
+          uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset();
+          if (MergeRegisterValues(branch_dex_pc)) {
+            work_list.push_back(branch_dex_pc);
+          }
+        }
+        dex_pc += inst.SizeInCodeUnits();
+      } else if ((opcode_flags & Instruction::kBranch) != 0) {
+        dex_pc += inst.GetTargetOffset();
+        DCHECK(IsBranchTarget(dex_pc));
+      } else {
+        break;
+      }
+
+      if (IsBranchTarget(dex_pc)) {
+        if (MergeRegisterValues(dex_pc)) {
+          current_registers_ = *dex_registers_[dex_pc].get();
+        } else {
+          break;
+        }
+      }
+    }
+  }
+}
+
+void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) {
+  switch (instruction.Opcode()) {
+    case Instruction::CONST_4:
+    case Instruction::CONST_16:
+    case Instruction::CONST:
+    case Instruction::CONST_HIGH16: {
+      int32_t register_index = instruction.VRegA();
+      UpdateRegister(register_index, VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::CONST_WIDE_16:
+    case Instruction::CONST_WIDE_32:
+    case Instruction::CONST_WIDE:
+    case Instruction::CONST_WIDE_HIGH16: {
+      int32_t register_index = instruction.VRegA();
+      UpdateRegister(register_index, VeriClass::long_);
+      break;
+    }
+
+    case Instruction::MOVE:
+    case Instruction::MOVE_FROM16:
+    case Instruction::MOVE_16: {
+      UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+      break;
+    }
+
+    case Instruction::MOVE_WIDE:
+    case Instruction::MOVE_WIDE_FROM16:
+    case Instruction::MOVE_WIDE_16: {
+      UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+      break;
+    }
+
+    case Instruction::MOVE_OBJECT:
+    case Instruction::MOVE_OBJECT_16:
+    case Instruction::MOVE_OBJECT_FROM16: {
+      UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+      break;
+    }
+    case Instruction::CONST_CLASS: {
+      UpdateRegister(instruction.VRegA_21c(),
+                     RegisterSource::kClass,
+                     VeriClass::class_,
+                     instruction.VRegB_21c());
+      break;
+    }
+    case Instruction::CONST_STRING: {
+      UpdateRegister(instruction.VRegA_21c(),
+                     RegisterSource::kString,
+                     VeriClass::string_,
+                     instruction.VRegB_21c());
+      break;
+    }
+
+    case Instruction::CONST_STRING_JUMBO: {
+      UpdateRegister(instruction.VRegA_31c(),
+                     RegisterSource::kString,
+                     VeriClass::string_,
+                     instruction.VRegB_31c());
+      break;
+    }
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_INTERFACE:
+    case Instruction::INVOKE_STATIC:
+    case Instruction::INVOKE_SUPER:
+    case Instruction::INVOKE_VIRTUAL: {
+      VeriMethod method = resolver_->GetMethod(instruction.VRegB_35c());
+      uint32_t args[5];
+      instruction.GetVarArgs(args);
+      if (method == VeriClass::forName_) {
+        RegisterValue value = GetRegister(args[0]);
+        last_result_ = RegisterValue(
+            value.GetSource(), value.GetDexFileReference(), VeriClass::class_);
+      } else if (IsGetField(method)) {
+        RegisterValue cls = GetRegister(args[0]);
+        RegisterValue name = GetRegister(args[1]);
+        field_uses_.push_back(std::make_pair(cls, name));
+        last_result_ = GetReturnType(instruction.VRegB_35c());
+      } else if (IsGetMethod(method)) {
+        RegisterValue cls = GetRegister(args[0]);
+        RegisterValue name = GetRegister(args[1]);
+        method_uses_.push_back(std::make_pair(cls, name));
+        last_result_ = GetReturnType(instruction.VRegB_35c());
+      } else if (method == VeriClass::getClass_) {
+        RegisterValue obj = GetRegister(args[0]);
+        last_result_ = RegisterValue(
+            obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_);
+      } else {
+        last_result_ = GetReturnType(instruction.VRegB_35c());
+      }
+      break;
+    }
+
+    case Instruction::INVOKE_DIRECT_RANGE:
+    case Instruction::INVOKE_INTERFACE_RANGE:
+    case Instruction::INVOKE_STATIC_RANGE:
+    case Instruction::INVOKE_SUPER_RANGE:
+    case Instruction::INVOKE_VIRTUAL_RANGE: {
+      last_result_ = GetReturnType(instruction.VRegB_3rc());
+      break;
+    }
+
+    case Instruction::MOVE_RESULT:
+    case Instruction::MOVE_RESULT_WIDE:
+    case Instruction::MOVE_RESULT_OBJECT: {
+      UpdateRegister(instruction.VRegA(), last_result_);
+      break;
+    }
+    case Instruction::RETURN_VOID:
+    case Instruction::RETURN_OBJECT:
+    case Instruction::RETURN_WIDE:
+    case Instruction::RETURN: {
+      break;
+    }
+    #define IF_XX(cond) \
+    case Instruction::IF_##cond: break; \
+    case Instruction::IF_##cond##Z: break
+
+    IF_XX(EQ);
+    IF_XX(NE);
+    IF_XX(LT);
+    IF_XX(LE);
+    IF_XX(GT);
+    IF_XX(GE);
+
+    case Instruction::GOTO:
+    case Instruction::GOTO_16:
+    case Instruction::GOTO_32: {
+      break;
+    }
+    case Instruction::INVOKE_POLYMORPHIC: {
+      // TODO
+      break;
+    }
+
+    case Instruction::INVOKE_POLYMORPHIC_RANGE: {
+      // TODO
+      break;
+    }
+
+    case Instruction::NEG_INT:
+    case Instruction::NEG_LONG:
+    case Instruction::NEG_FLOAT:
+    case Instruction::NEG_DOUBLE:
+    case Instruction::NOT_INT:
+    case Instruction::NOT_LONG: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::INT_TO_LONG:
+    case Instruction::INT_TO_FLOAT:
+    case Instruction::INT_TO_DOUBLE:
+    case Instruction::LONG_TO_INT:
+    case Instruction::LONG_TO_FLOAT:
+    case Instruction::LONG_TO_DOUBLE:
+    case Instruction::FLOAT_TO_INT:
+    case Instruction::FLOAT_TO_LONG:
+    case Instruction::FLOAT_TO_DOUBLE:
+    case Instruction::DOUBLE_TO_INT:
+    case Instruction::DOUBLE_TO_LONG:
+    case Instruction::DOUBLE_TO_FLOAT:
+    case Instruction::INT_TO_BYTE:
+    case Instruction::INT_TO_SHORT:
+    case Instruction::INT_TO_CHAR: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::ADD_INT:
+    case Instruction::ADD_LONG:
+    case Instruction::ADD_DOUBLE:
+    case Instruction::ADD_FLOAT:
+    case Instruction::SUB_INT:
+    case Instruction::SUB_LONG:
+    case Instruction::SUB_FLOAT:
+    case Instruction::SUB_DOUBLE:
+    case Instruction::MUL_INT:
+    case Instruction::MUL_LONG:
+    case Instruction::MUL_FLOAT:
+    case Instruction::MUL_DOUBLE:
+    case Instruction::DIV_INT:
+    case Instruction::DIV_LONG:
+    case Instruction::DIV_FLOAT:
+    case Instruction::DIV_DOUBLE:
+    case Instruction::REM_INT:
+    case Instruction::REM_LONG:
+    case Instruction::REM_FLOAT:
+    case Instruction::REM_DOUBLE:
+    case Instruction::AND_INT:
+    case Instruction::AND_LONG:
+    case Instruction::SHL_INT:
+    case Instruction::SHL_LONG:
+    case Instruction::SHR_INT:
+    case Instruction::SHR_LONG:
+    case Instruction::USHR_INT:
+    case Instruction::USHR_LONG:
+    case Instruction::OR_INT:
+    case Instruction::OR_LONG:
+    case Instruction::XOR_INT:
+    case Instruction::XOR_LONG: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::ADD_INT_2ADDR:
+    case Instruction::ADD_LONG_2ADDR:
+    case Instruction::ADD_DOUBLE_2ADDR:
+    case Instruction::ADD_FLOAT_2ADDR:
+    case Instruction::SUB_INT_2ADDR:
+    case Instruction::SUB_LONG_2ADDR:
+    case Instruction::SUB_FLOAT_2ADDR:
+    case Instruction::SUB_DOUBLE_2ADDR:
+    case Instruction::MUL_INT_2ADDR:
+    case Instruction::MUL_LONG_2ADDR:
+    case Instruction::MUL_FLOAT_2ADDR:
+    case Instruction::MUL_DOUBLE_2ADDR:
+    case Instruction::DIV_INT_2ADDR:
+    case Instruction::DIV_LONG_2ADDR:
+    case Instruction::REM_INT_2ADDR:
+    case Instruction::REM_LONG_2ADDR:
+    case Instruction::REM_FLOAT_2ADDR:
+    case Instruction::REM_DOUBLE_2ADDR:
+    case Instruction::SHL_INT_2ADDR:
+    case Instruction::SHL_LONG_2ADDR:
+    case Instruction::SHR_INT_2ADDR:
+    case Instruction::SHR_LONG_2ADDR:
+    case Instruction::USHR_INT_2ADDR:
+    case Instruction::USHR_LONG_2ADDR:
+    case Instruction::DIV_FLOAT_2ADDR:
+    case Instruction::DIV_DOUBLE_2ADDR:
+    case Instruction::AND_INT_2ADDR:
+    case Instruction::AND_LONG_2ADDR:
+    case Instruction::OR_INT_2ADDR:
+    case Instruction::OR_LONG_2ADDR:
+    case Instruction::XOR_INT_2ADDR:
+    case Instruction::XOR_LONG_2ADDR: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::ADD_INT_LIT16:
+    case Instruction::AND_INT_LIT16:
+    case Instruction::OR_INT_LIT16:
+    case Instruction::XOR_INT_LIT16:
+    case Instruction::RSUB_INT:
+    case Instruction::MUL_INT_LIT16:
+    case Instruction::DIV_INT_LIT16:
+    case Instruction::REM_INT_LIT16: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::ADD_INT_LIT8:
+    case Instruction::AND_INT_LIT8:
+    case Instruction::OR_INT_LIT8:
+    case Instruction::XOR_INT_LIT8:
+    case Instruction::RSUB_INT_LIT8:
+    case Instruction::MUL_INT_LIT8:
+    case Instruction::DIV_INT_LIT8:
+    case Instruction::REM_INT_LIT8:
+    case Instruction::SHL_INT_LIT8:
+    case Instruction::SHR_INT_LIT8: {
+    case Instruction::USHR_INT_LIT8: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::NEW_INSTANCE: {
+      VeriClass* cls = resolver_->GetVeriClass(dex::TypeIndex(instruction.VRegB_21c()));
+      UpdateRegister(instruction.VRegA(), cls);
+      break;
+    }
+
+    case Instruction::NEW_ARRAY: {
+      dex::TypeIndex type_index(instruction.VRegC_22c());
+      VeriClass* cls = resolver_->GetVeriClass(type_index);
+      UpdateRegister(instruction.VRegA_22c(), cls);
+      break;
+    }
+
+    case Instruction::FILLED_NEW_ARRAY: {
+      dex::TypeIndex type_index(instruction.VRegB_35c());
+      VeriClass* cls = resolver_->GetVeriClass(type_index);
+      UpdateRegister(instruction.VRegA_22c(), cls);
+      break;
+    }
+
+    case Instruction::FILLED_NEW_ARRAY_RANGE: {
+      dex::TypeIndex type_index(instruction.VRegB_3rc());
+      uint32_t register_index = instruction.VRegC_3rc();
+      VeriClass* cls = resolver_->GetVeriClass(type_index);
+      UpdateRegister(register_index, cls);
+      break;
+    }
+
+    case Instruction::FILL_ARRAY_DATA: {
+      break;
+    }
+
+    case Instruction::CMP_LONG:
+    case Instruction::CMPG_FLOAT:
+    case Instruction::CMPG_DOUBLE:
+    case Instruction::CMPL_FLOAT:
+    case Instruction::CMPL_DOUBLE:
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::NOP:
+      break;
+
+    case Instruction::IGET:
+    case Instruction::IGET_WIDE:
+    case Instruction::IGET_OBJECT:
+    case Instruction::IGET_BOOLEAN:
+    case Instruction::IGET_BYTE:
+    case Instruction::IGET_CHAR:
+    case Instruction::IGET_SHORT: {
+      UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+      break;
+    }
+
+    case Instruction::IPUT:
+    case Instruction::IPUT_WIDE:
+    case Instruction::IPUT_OBJECT:
+    case Instruction::IPUT_BOOLEAN:
+    case Instruction::IPUT_BYTE:
+    case Instruction::IPUT_CHAR:
+    case Instruction::IPUT_SHORT: {
+      break;
+    }
+
+    case Instruction::SGET:
+    case Instruction::SGET_WIDE:
+    case Instruction::SGET_OBJECT:
+    case Instruction::SGET_BOOLEAN:
+    case Instruction::SGET_BYTE:
+    case Instruction::SGET_CHAR:
+    case Instruction::SGET_SHORT: {
+      UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+      break;
+    }
+
+    case Instruction::SPUT:
+    case Instruction::SPUT_WIDE:
+    case Instruction::SPUT_OBJECT:
+    case Instruction::SPUT_BOOLEAN:
+    case Instruction::SPUT_BYTE:
+    case Instruction::SPUT_CHAR:
+    case Instruction::SPUT_SHORT: {
+      break;
+    }
+
+#define ARRAY_XX(kind, anticipated_type)                                          \
+    case Instruction::AGET##kind: {                                               \
+      UpdateRegister(instruction.VRegA_23x(), anticipated_type);                  \
+      break;                                                                      \
+    }                                                                             \
+    case Instruction::APUT##kind: {                                               \
+      break;                                                                      \
+    }
+
+    ARRAY_XX(, VeriClass::integer_);
+    ARRAY_XX(_WIDE, VeriClass::long_);
+    ARRAY_XX(_BOOLEAN, VeriClass::boolean_);
+    ARRAY_XX(_BYTE, VeriClass::byte_);
+    ARRAY_XX(_CHAR, VeriClass::char_);
+    ARRAY_XX(_SHORT, VeriClass::short_);
+
+    case Instruction::AGET_OBJECT: {
+      // TODO: take the component type.
+      UpdateRegister(instruction.VRegA_23x(), VeriClass::object_);
+      break;
+    }
+
+    case Instruction::APUT_OBJECT: {
+      break;
+    }
+
+    case Instruction::ARRAY_LENGTH: {
+      UpdateRegister(instruction.VRegA_12x(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::MOVE_EXCEPTION: {
+      UpdateRegister(instruction.VRegA_11x(), VeriClass::throwable_);
+      break;
+    }
+
+    case Instruction::THROW: {
+      break;
+    }
+
+    case Instruction::INSTANCE_OF: {
+      uint8_t destination = instruction.VRegA_22c();
+      UpdateRegister(destination, VeriClass::boolean_);
+      break;
+    }
+
+    case Instruction::CHECK_CAST: {
+      uint8_t reference = instruction.VRegA_21c();
+      dex::TypeIndex type_index(instruction.VRegB_21c());
+      UpdateRegister(reference, resolver_->GetVeriClass(type_index));
+      break;
+    }
+
+    case Instruction::MONITOR_ENTER:
+    case Instruction::MONITOR_EXIT: {
+      break;
+    }
+
+    case Instruction::SPARSE_SWITCH:
+    case Instruction::PACKED_SWITCH:
+      break;
+
+    default:
+      break;
+  }
+}
+
+void VeriFlowAnalysis::Run() {
+  FindBranches();
+  AnalyzeCode();
+}
+
+}  // namespace art
diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h
new file mode 100644
index 0000000..c065fb8
--- /dev/null
+++ b/tools/veridex/flow_analysis.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 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_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
+#define ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
+
+#include "dex/code_item_accessors.h"
+#include "dex/dex_file_reference.h"
+#include "dex/method_reference.h"
+#include "veridex.h"
+
+namespace art {
+
+class VeridexClass;
+class VeridexResolver;
+
+/**
+ * The source where a dex register comes from.
+ */
+enum class RegisterSource {
+  kParameter,
+  kField,
+  kMethod,
+  kClass,
+  kString,
+  kNone
+};
+
+/**
+ * Abstract representation of a dex register value.
+ */
+class RegisterValue {
+ public:
+  RegisterValue() : source_(RegisterSource::kNone), reference_(nullptr, 0), type_(nullptr) {}
+  RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type)
+      : source_(source), reference_(reference), type_(type) {}
+
+  RegisterSource GetSource() const { return source_; }
+  DexFileReference GetDexFileReference() const { return reference_; }
+  const VeriClass* GetType() const { return type_; }
+
+  const char* ToString() const {
+    switch (source_) {
+      case RegisterSource::kString:
+        return reference_.dex_file->StringDataByIdx(dex::StringIndex(reference_.index));
+      case RegisterSource::kClass:
+        return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index));
+      default:
+        return "<unknown>";
+    }
+  }
+
+ private:
+  RegisterSource source_;
+  DexFileReference reference_;
+  const VeriClass* type_;
+};
+
+struct InstructionInfo {
+  bool has_been_visited;
+};
+
+class VeriFlowAnalysis {
+ public:
+  VeriFlowAnalysis(VeridexResolver* resolver,
+                   const CodeItemDataAccessor& code_item_accessor)
+      : resolver_(resolver),
+        code_item_accessor_(code_item_accessor),
+        dex_registers_(code_item_accessor.InsnsSizeInCodeUnits()),
+        instruction_infos_(code_item_accessor.InsnsSizeInCodeUnits()) {}
+
+  void Run();
+
+  const std::vector<std::pair<RegisterValue, RegisterValue>>& GetFieldUses() const {
+    return field_uses_;
+  }
+
+  const std::vector<std::pair<RegisterValue, RegisterValue>>& GetMethodUses() const {
+    return method_uses_;
+  }
+
+ private:
+  // Find all branches in the code.
+  void FindBranches();
+
+  // Analyze all non-deead instructions in the code.
+  void AnalyzeCode();
+
+  // Set the instruction at the given pc as a branch target.
+  void SetAsBranchTarget(uint32_t dex_pc);
+
+  // Whether the instruction at the given pc is a branch target.
+  bool IsBranchTarget(uint32_t dex_pc);
+
+  // Merge the register values at the given pc with `current_registers`.
+  // Return whether the register values have changed, and the instruction needs
+  // to be visited again.
+  bool MergeRegisterValues(uint32_t dex_pc);
+
+  void UpdateRegister(
+      uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id);
+  void UpdateRegister(uint32_t dex_register, const RegisterValue& value);
+  void UpdateRegister(uint32_t dex_register, const VeriClass* cls);
+  const RegisterValue& GetRegister(uint32_t dex_register);
+  void ProcessDexInstruction(const Instruction& inst);
+  void SetVisited(uint32_t dex_pc);
+  RegisterValue GetReturnType(uint32_t method_index);
+  RegisterValue GetFieldType(uint32_t field_index);
+
+  VeridexResolver* resolver_;
+  const CodeItemDataAccessor& code_item_accessor_;
+
+  // Vector of register values for all branch targets.
+  std::vector<std::unique_ptr<std::vector<RegisterValue>>> dex_registers_;
+
+  // The current values of dex registers.
+  std::vector<RegisterValue> current_registers_;
+
+  // Information on each instruction useful for the analysis.
+  std::vector<InstructionInfo> instruction_infos_;
+
+  // The value of invoke instructions, to be fetched when visiting move-result.
+  RegisterValue last_result_;
+
+  // List of reflection field uses found.
+  std::vector<std::pair<RegisterValue, RegisterValue>> field_uses_;
+
+  // List of reflection method uses found.
+  std::vector<std::pair<RegisterValue, RegisterValue>> method_uses_;
+};
+
+}  // namespace art
+
+#endif  // ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc
index 33e499b..17fa1b8 100644
--- a/tools/veridex/hidden_api.cc
+++ b/tools/veridex/hidden_api.cc
@@ -44,17 +44,6 @@
   return ss.str();
 }
 
-bool HiddenApi::LogIfIn(const std::string& name,
-                        const std::set<std::string>& list,
-                        const std::string& log,
-                        const std::string& access_kind) {
-  if (list.find(name) != list.end()) {
-    LOG(WARNING) << std::string(log) << " usage found " << name << " (" << access_kind << ")";
-    return true;
-  }
-  return false;
-}
-
 void HiddenApi::FillList(const char* filename, std::set<std::string>& entries) {
   if (filename == nullptr) {
     return;
@@ -72,6 +61,11 @@
         // Add the class->method name (so stripping the signature).
         entries.insert(str.substr(0, pos));
       }
+      pos = str.find(':');
+      if (pos != std::string::npos) {
+        // Add the class->field name (so stripping the type).
+        entries.insert(str.substr(0, pos));
+      }
     }
   }
 }
diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h
index 282e7cf..4c67768 100644
--- a/tools/veridex/hidden_api.h
+++ b/tools/veridex/hidden_api.h
@@ -17,6 +17,10 @@
 #ifndef ART_TOOLS_VERIDEX_HIDDEN_API_H_
 #define ART_TOOLS_VERIDEX_HIDDEN_API_H_
 
+#include "dex/hidden_api_access_flags.h"
+#include "dex/method_reference.h"
+
+#include <ostream>
 #include <set>
 #include <string>
 
@@ -35,21 +39,34 @@
     FillList(blacklist, blacklist_);
   }
 
-  bool LogIfInList(const std::string& name, const char* access_kind) const {
-    return LogIfIn(name, blacklist_, "Blacklist", access_kind) ||
-        LogIfIn(name, dark_greylist_, "Dark greylist", access_kind) ||
-        LogIfIn(name, light_greylist_, "Light greylist", access_kind);
+  HiddenApiAccessFlags::ApiList GetApiList(const std::string& name) const {
+    if (IsInList(name, blacklist_)) {
+      return HiddenApiAccessFlags::kBlacklist;
+    } else if (IsInList(name, dark_greylist_)) {
+      return HiddenApiAccessFlags::kDarkGreylist;
+    } else if (IsInList(name, light_greylist_)) {
+      return HiddenApiAccessFlags::kLightGreylist;
+    } else {
+      return HiddenApiAccessFlags::kWhitelist;
+    }
+  }
+
+  bool IsInRestrictionList(const std::string& name) const {
+    return GetApiList(name) != HiddenApiAccessFlags::kWhitelist;
   }
 
   static std::string GetApiMethodName(const DexFile& dex_file, uint32_t method_index);
 
   static std::string GetApiFieldName(const DexFile& dex_file, uint32_t field_index);
 
+  static std::string GetApiMethodName(MethodReference ref) {
+    return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index);
+  }
+
  private:
-  static bool LogIfIn(const std::string& name,
-                      const std::set<std::string>& list,
-                      const std::string& log,
-                      const std::string& access_kind);
+  static bool IsInList(const std::string& name, const std::set<std::string>& list) {
+    return list.find(name) != list.end();
+  }
 
   static void FillList(const char* filename, std::set<std::string>& entries);
 
@@ -58,6 +75,13 @@
   std::set<std::string> dark_greylist_;
 };
 
+struct HiddenApiStats {
+  uint32_t count = 0;
+  uint32_t reflection_count = 0;
+  uint32_t linking_count = 0;
+  uint32_t api_counts[4] = { 0, 0, 0, 0 };
+};
+
 }  // namespace art
 
 #endif  // ART_TOOLS_VERIDEX_HIDDEN_API_H_
diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc
new file mode 100644
index 0000000..b9be618
--- /dev/null
+++ b/tools/veridex/hidden_api_finder.cc
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "hidden_api_finder.h"
+
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_file.h"
+#include "dex/method_reference.h"
+#include "hidden_api.h"
+#include "resolver.h"
+#include "veridex.h"
+
+#include <iostream>
+
+namespace art {
+
+void HiddenApiFinder::CheckMethod(uint32_t method_id,
+                                  VeridexResolver* resolver,
+                                  MethodReference ref) {
+  // Cheap check that the method is resolved. If it is, we know it's not in
+  // a restricted list.
+  if (resolver->GetMethod(method_id) != nullptr) {
+    return;
+  }
+  std::string name = HiddenApi::GetApiMethodName(resolver->GetDexFile(), method_id);
+  if (hidden_api_.IsInRestrictionList(name)) {
+    method_locations_[name].push_back(ref);
+  }
+}
+
+void HiddenApiFinder::CheckField(uint32_t field_id,
+                                 VeridexResolver* resolver,
+                                 MethodReference ref) {
+  // Cheap check that the field is resolved. If it is, we know it's not in
+  // a restricted list.
+  if (resolver->GetField(field_id) != nullptr) {
+    return;
+  }
+  std::string name = HiddenApi::GetApiFieldName(resolver->GetDexFile(), field_id);
+  if (hidden_api_.IsInRestrictionList(name)) {
+    field_locations_[name].push_back(ref);
+  }
+}
+
+void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) {
+  const DexFile& dex_file = resolver->GetDexFile();
+  // Look at all types referenced in this dex file. Any of these
+  // types can lead to being used through reflection.
+  for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) {
+    std::string name(dex_file.StringByTypeIdx(dex::TypeIndex(i)));
+    if (hidden_api_.IsInRestrictionList(name)) {
+      classes_.insert(name);
+    }
+  }
+  // Note: we collect strings constants only referenced in code items as the string table
+  // contains other kind of strings (eg types).
+  size_t class_def_count = dex_file.NumClassDefs();
+  for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+    const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+    const uint8_t* class_data = dex_file.GetClassData(class_def);
+    if (class_data == nullptr) {
+      // Empty class.
+      continue;
+    }
+    ClassDataItemIterator it(dex_file, class_data);
+    it.SkipAllFields();
+    for (; it.HasNextMethod(); it.Next()) {
+      const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+      if (code_item == nullptr) {
+        continue;
+      }
+      CodeItemDataAccessor code_item_accessor(dex_file, code_item);
+      for (const DexInstructionPcPair& inst : code_item_accessor) {
+        switch (inst->Opcode()) {
+          case Instruction::CONST_STRING: {
+            dex::StringIndex string_index(inst->VRegB_21c());
+            std::string name = std::string(dex_file.StringDataByIdx(string_index));
+            // Cheap filtering on the string literal. We know it cannot be a field/method/class
+            // if it contains a space.
+            if (name.find(' ') == std::string::npos) {
+              // Class names at the Java level are of the form x.y.z, but the list encodes
+              // them of the form Lx/y/z;. Inner classes have '$' for both Java level class
+              // names in strings, and hidden API lists.
+              std::string str = name;
+              std::replace(str.begin(), str.end(), '.', '/');
+              str = "L" + str + ";";
+              // Note: we can query the lists directly, as HiddenApi added classes that own
+              // private methods and fields in them.
+              // We don't add class names to the `strings_` set as we know method/field names
+              // don't have '.' or '/'. All hidden API class names have a '/'.
+              if (hidden_api_.IsInRestrictionList(str)) {
+                classes_.insert(str);
+              } else if (hidden_api_.IsInRestrictionList(name)) {
+                // Could be something passed to JNI.
+                classes_.insert(name);
+              } else {
+                // We only keep track of the location for strings, as these will be the
+                // field/method names the user is interested in.
+                strings_.insert(name);
+                reflection_locations_[name].push_back(
+                    MethodReference(&dex_file, it.GetMemberIndex()));
+              }
+            }
+            break;
+          }
+          case Instruction::INVOKE_DIRECT:
+          case Instruction::INVOKE_INTERFACE:
+          case Instruction::INVOKE_STATIC:
+          case Instruction::INVOKE_SUPER:
+          case Instruction::INVOKE_VIRTUAL: {
+            CheckMethod(
+                inst->VRegB_35c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          case Instruction::INVOKE_DIRECT_RANGE:
+          case Instruction::INVOKE_INTERFACE_RANGE:
+          case Instruction::INVOKE_STATIC_RANGE:
+          case Instruction::INVOKE_SUPER_RANGE:
+          case Instruction::INVOKE_VIRTUAL_RANGE: {
+            CheckMethod(
+                inst->VRegB_3rc(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          case Instruction::IGET:
+          case Instruction::IGET_WIDE:
+          case Instruction::IGET_OBJECT:
+          case Instruction::IGET_BOOLEAN:
+          case Instruction::IGET_BYTE:
+          case Instruction::IGET_CHAR:
+          case Instruction::IGET_SHORT: {
+            CheckField(
+                inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          case Instruction::IPUT:
+          case Instruction::IPUT_WIDE:
+          case Instruction::IPUT_OBJECT:
+          case Instruction::IPUT_BOOLEAN:
+          case Instruction::IPUT_BYTE:
+          case Instruction::IPUT_CHAR:
+          case Instruction::IPUT_SHORT: {
+            CheckField(
+                inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          case Instruction::SGET:
+          case Instruction::SGET_WIDE:
+          case Instruction::SGET_OBJECT:
+          case Instruction::SGET_BOOLEAN:
+          case Instruction::SGET_BYTE:
+          case Instruction::SGET_CHAR:
+          case Instruction::SGET_SHORT: {
+            CheckField(
+                inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          case Instruction::SPUT:
+          case Instruction::SPUT_WIDE:
+          case Instruction::SPUT_OBJECT:
+          case Instruction::SPUT_BOOLEAN:
+          case Instruction::SPUT_BYTE:
+          case Instruction::SPUT_CHAR:
+          case Instruction::SPUT_SHORT: {
+            CheckField(
+                inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          default:
+            break;
+        }
+      }
+    }
+  }
+}
+
+void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
+  for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
+    CollectAccesses(resolver.get());
+  }
+}
+
+void HiddenApiFinder::Dump(std::ostream& os,
+                           HiddenApiStats* stats,
+                           bool dump_reflection) {
+  static const char* kPrefix = "       ";
+  stats->linking_count = method_locations_.size() + field_locations_.size();
+
+  // Dump methods from hidden APIs linked against.
+  for (const std::pair<std::string, std::vector<MethodReference>>& pair : method_locations_) {
+    HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first);
+    stats->api_counts[api_list]++;
+    os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
+    os << std::endl;
+    for (const MethodReference& ref : pair.second) {
+      os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+    }
+    os << std::endl;
+  }
+
+  // Dump fields from hidden APIs linked against.
+  for (const std::pair<std::string, std::vector<MethodReference>>& pair : field_locations_) {
+    HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first);
+    stats->api_counts[api_list]++;
+    os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
+    os << std::endl;
+    for (const MethodReference& ref : pair.second) {
+      os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+    }
+    os << std::endl;
+  }
+
+  if (dump_reflection) {
+    // Dump potential reflection uses.
+    for (const std::string& cls : classes_) {
+      for (const std::string& name : strings_) {
+        std::string full_name = cls + "->" + name;
+        HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+        stats->api_counts[api_list]++;
+        if (api_list != HiddenApiAccessFlags::kWhitelist) {
+          stats->reflection_count++;
+          os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
+             << " potential use(s):";
+          os << std::endl;
+          for (const MethodReference& ref : reflection_locations_[name]) {
+            os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+          }
+          os << std::endl;
+        }
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/tools/veridex/hidden_api_finder.h b/tools/veridex/hidden_api_finder.h
new file mode 100644
index 0000000..f7d3dc8
--- /dev/null
+++ b/tools/veridex/hidden_api_finder.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_
+#define ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_
+
+#include "dex/method_reference.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+
+namespace art {
+
+class HiddenApi;
+struct HiddenApiStats;
+class VeridexResolver;
+
+/**
+ * Reports potential uses of hidden APIs from static linking and reflection.
+ */
+class HiddenApiFinder {
+ public:
+  explicit HiddenApiFinder(const HiddenApi& hidden_api) : hidden_api_(hidden_api) {}
+
+  // Iterate over the dex files associated with the passed resolvers to report
+  // hidden API uses.
+  void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers);
+
+  void Dump(std::ostream& os, HiddenApiStats* stats, bool dump_reflection);
+
+ private:
+  void CollectAccesses(VeridexResolver* resolver);
+  void CheckMethod(uint32_t method_idx, VeridexResolver* resolver, MethodReference ref);
+  void CheckField(uint32_t field_idx, VeridexResolver* resolver, MethodReference ref);
+
+  const HiddenApi& hidden_api_;
+  std::set<std::string> classes_;
+  std::set<std::string> strings_;
+  std::map<std::string, std::vector<MethodReference>> reflection_locations_;
+  std::map<std::string, std::vector<MethodReference>> method_locations_;
+  std::map<std::string, std::vector<MethodReference>> field_locations_;
+};
+
+}  // namespace art
+
+#endif  // ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_
diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc
new file mode 100644
index 0000000..2092af3
--- /dev/null
+++ b/tools/veridex/precise_hidden_api_finder.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "precise_hidden_api_finder.h"
+
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_file.h"
+#include "dex/method_reference.h"
+#include "flow_analysis.h"
+#include "hidden_api.h"
+#include "resolver.h"
+#include "veridex.h"
+
+#include <iostream>
+
+namespace art {
+
+void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
+  for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
+    const DexFile& dex_file = resolver->GetDexFile();
+    size_t class_def_count = dex_file.NumClassDefs();
+    for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+      const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+      const uint8_t* class_data = dex_file.GetClassData(class_def);
+      if (class_data == nullptr) {
+        // Empty class.
+        continue;
+      }
+      ClassDataItemIterator it(dex_file, class_data);
+      it.SkipAllFields();
+      for (; it.HasNextMethod(); it.Next()) {
+        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+        if (code_item == nullptr) {
+          continue;
+        }
+        CodeItemDataAccessor code_item_accessor(dex_file, code_item);
+        VeriFlowAnalysis ana(resolver.get(), code_item_accessor);
+        ana.Run();
+        if (!ana.GetFieldUses().empty()) {
+          field_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetFieldUses();
+        }
+        if (!ana.GetMethodUses().empty()) {
+          method_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetMethodUses();
+        }
+      }
+    }
+  }
+}
+
+void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) {
+  static const char* kPrefix = "       ";
+  for (auto kinds : { field_uses_, method_uses_ }) {
+    for (auto it : kinds) {
+      MethodReference ref = it.first;
+      for (const std::pair<RegisterValue, RegisterValue>& info : it.second) {
+        if ((info.first.GetSource() == RegisterSource::kClass ||
+             info.first.GetSource() == RegisterSource::kString) &&
+            info.second.GetSource() == RegisterSource::kString) {
+          std::string cls(info.first.ToString());
+          std::string name(info.second.ToString());
+          std::string full_name = cls + "->" + name;
+          HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+          stats->api_counts[api_list]++;
+          if (api_list != HiddenApiAccessFlags::kWhitelist) {
+            ++stats->reflection_count;
+            os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
+               << " use:";
+            os << std::endl;
+            os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+            os << std::endl;
+          }
+        }
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h
new file mode 100644
index 0000000..22744a6
--- /dev/null
+++ b/tools/veridex/precise_hidden_api_finder.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
+#define ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
+
+#include "dex/method_reference.h"
+#include "flow_analysis.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+
+namespace art {
+
+class HiddenApi;
+struct HiddenApiStats;
+class VeridexResolver;
+
+/**
+ * Reports known uses of hidden APIs from reflection.
+ */
+class PreciseHiddenApiFinder {
+ public:
+  explicit PreciseHiddenApiFinder(const HiddenApi& hidden_api) : hidden_api_(hidden_api) {}
+
+  // Iterate over the dex files associated with the passed resolvers to report
+  // hidden API uses.
+  void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers);
+
+  void Dump(std::ostream& os, HiddenApiStats* stats);
+
+ private:
+  const HiddenApi& hidden_api_;
+  std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> field_uses_;
+  std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> method_uses_;
+};
+
+}  // namespace art
+
+#endif  // ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc
index 6ab872e..9113039 100644
--- a/tools/veridex/resolver.cc
+++ b/tools/veridex/resolver.cc
@@ -59,6 +59,14 @@
 static bool HasSameNameAndSignature(const DexFile& dex_file,
                                     const DexFile::MethodId& method_id,
                                     const char* method_name,
+                                    const char* type) {
+  return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 &&
+      strcmp(type, dex_file.GetMethodSignature(method_id).ToString().c_str()) == 0;
+}
+
+static bool HasSameNameAndSignature(const DexFile& dex_file,
+                                    const DexFile::MethodId& method_id,
+                                    const char* method_name,
                                     const Signature& signature) {
   return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 &&
       dex_file.GetMethodSignature(method_id) == signature;
@@ -241,6 +249,34 @@
   return nullptr;
 }
 
+VeriMethod VeridexResolver::LookupDeclaredMethodIn(const VeriClass& kls,
+                                                   const char* method_name,
+                                                   const char* type) const {
+  if (kls.IsPrimitive()) {
+    return nullptr;
+  }
+  if (kls.IsArray()) {
+    return nullptr;
+  }
+  VeridexResolver* resolver = GetResolverOf(kls);
+  const DexFile& other_dex_file = resolver->dex_file_;
+  const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef());
+  if (class_data != nullptr) {
+    ClassDataItemIterator it(other_dex_file, class_data);
+    it.SkipAllFields();
+    for (; it.HasNextMethod(); it.Next()) {
+      const DexFile::MethodId& other_method_id = other_dex_file.GetMethodId(it.GetMemberIndex());
+      if (HasSameNameAndSignature(other_dex_file,
+                                  other_method_id,
+                                  method_name,
+                                  type)) {
+        return it.DataPointer();
+      }
+    }
+  }
+  return nullptr;
+}
+
 VeriMethod VeridexResolver::GetMethod(uint32_t method_index) {
   VeriMethod method_info = method_infos_[method_index];
   if (method_info == nullptr) {
@@ -277,10 +313,8 @@
   return field_info;
 }
 
-void VeridexResolver::ResolveAll(const HiddenApi& hidden_api) {
+void VeridexResolver::ResolveAll() {
   for (uint32_t i = 0; i < dex_file_.NumTypeIds(); ++i) {
-    // Note: we don't look at HiddenApi for types, as the lists don't contain
-    // classes.
     if (GetVeriClass(dex::TypeIndex(i)) == nullptr) {
       LOG(WARNING) << "Unresolved " << dex_file_.PrettyType(dex::TypeIndex(i));
     }
@@ -288,17 +322,13 @@
 
   for (uint32_t i = 0; i < dex_file_.NumMethodIds(); ++i) {
     if (GetMethod(i) == nullptr) {
-      if (!hidden_api.LogIfInList(HiddenApi::GetApiMethodName(dex_file_, i), "Linking")) {
-        LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i);
-      }
+      LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i);
     }
   }
 
   for (uint32_t i = 0; i < dex_file_.NumFieldIds(); ++i) {
     if (GetField(i) == nullptr) {
-      if (!hidden_api.LogIfInList(HiddenApi::GetApiFieldName(dex_file_, i), "Linking")) {
-        LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i);
-      }
+      LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i);
     }
   }
 }
diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h
index 82f6aae..52b15fe 100644
--- a/tools/veridex/resolver.h
+++ b/tools/veridex/resolver.h
@@ -66,9 +66,18 @@
                           const char* field_name,
                           const char* field_type);
 
-  // Resolve all type_id/method_id/field_id. Log for unresolved
-  // entities, or entities part of a hidden API list.
-  void ResolveAll(const HiddenApi& hidden_api);
+  // Lookup a method declared in `kls`.
+  VeriMethod LookupDeclaredMethodIn(const VeriClass& kls,
+                                    const char* method_name,
+                                    const char* signature) const;
+
+  // Resolve all type_id/method_id/field_id.
+  void ResolveAll();
+
+  // The dex file this resolver is associated to.
+  const DexFile& GetDexFile() const {
+    return dex_file_;
+  }
 
  private:
   // Return the resolver where `kls` is from.
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index c5203fe..6e72faa 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -21,6 +21,8 @@
 #include "dex/dex_file.h"
 #include "dex/dex_file_loader.h"
 #include "hidden_api.h"
+#include "hidden_api_finder.h"
+#include "precise_hidden_api_finder.h"
 #include "resolver.h"
 
 #include <sstream>
@@ -46,8 +48,18 @@
 VeriClass* VeriClass::double_ = &d_;
 VeriClass* VeriClass::long_ = &j_;
 VeriClass* VeriClass::void_ = &v_;
+
 // Will be set after boot classpath has been resolved.
 VeriClass* VeriClass::object_ = nullptr;
+VeriClass* VeriClass::class_ = nullptr;
+VeriClass* VeriClass::string_ = nullptr;
+VeriClass* VeriClass::throwable_ = nullptr;
+VeriMethod VeriClass::forName_ = nullptr;
+VeriMethod VeriClass::getField_ = nullptr;
+VeriMethod VeriClass::getDeclaredField_ = nullptr;
+VeriMethod VeriClass::getMethod_ = nullptr;
+VeriMethod VeriClass::getDeclaredMethod_ = nullptr;
+VeriMethod VeriClass::getClass_ = nullptr;
 
 struct VeridexOptions {
   const char* dex_file = nullptr;
@@ -55,6 +67,7 @@
   const char* blacklist = nullptr;
   const char* light_greylist = nullptr;
   const char* dark_greylist = nullptr;
+  bool precise = true;
 };
 
 static const char* Substr(const char* str, int index) {
@@ -75,6 +88,7 @@
   static const char* kBlacklistOption = "--blacklist=";
   static const char* kDarkGreylistOption = "--dark-greylist=";
   static const char* kLightGreylistOption = "--light-greylist=";
+  static const char* kImprecise = "--imprecise";
 
   for (int i = 0; i < argc; ++i) {
     if (StartsWith(argv[i], kDexFileOption)) {
@@ -87,6 +101,8 @@
       options->dark_greylist = Substr(argv[i], strlen(kDarkGreylistOption));
     } else if (StartsWith(argv[i], kLightGreylistOption)) {
       options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption));
+    } else if (strcmp(argv[i], kImprecise) == 0) {
+      options->precise = false;
     }
   }
 }
@@ -156,22 +172,70 @@
     std::vector<std::unique_ptr<VeridexResolver>> boot_resolvers;
     Resolve(boot_dex_files, resolver_map, type_map, &boot_resolvers);
 
-    // Now that boot classpath has been resolved, fill j.l.Object.
+    // Now that boot classpath has been resolved, fill classes and reflection
+    // methods.
     VeriClass::object_ = type_map["Ljava/lang/Object;"];
+    VeriClass::class_ = type_map["Ljava/lang/Class;"];
+    VeriClass::string_ = type_map["Ljava/lang/String;"];
+    VeriClass::throwable_ = type_map["Ljava/lang/Throwable;"];
+    VeriClass::forName_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::class_, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
+    VeriClass::getField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::class_, "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
+    VeriClass::getDeclaredField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::class_, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
+    VeriClass::getMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::class_,
+        "getMethod",
+        "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
+    VeriClass::getDeclaredMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::class_,
+        "getDeclaredMethod",
+        "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
+    VeriClass::getClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::object_, "getClass", "()Ljava/lang/Class;");
 
     std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
     Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
 
-    // Resolve all type_id/method_id/field_id of app dex files.
+    // Find and log uses of hidden APIs.
     HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist);
-    for (const std::unique_ptr<VeridexResolver>& resolver : app_resolvers) {
-      resolver->ResolveAll(hidden_api);
+    HiddenApiStats stats;
+
+    HiddenApiFinder api_finder(hidden_api);
+    api_finder.Run(app_resolvers);
+    api_finder.Dump(std::cout, &stats, !options.precise);
+
+    if (options.precise) {
+      PreciseHiddenApiFinder precise_api_finder(hidden_api);
+      precise_api_finder.Run(app_resolvers);
+      precise_api_finder.Dump(std::cout, &stats);
+    }
+
+    DumpSummaryStats(std::cout, stats);
+
+    if (options.precise) {
+      std::cout << "To run an analysis that can give more reflection accesses, " << std::endl
+                << "but could include false positives, pass the --imprecise flag. " << std::endl;
     }
 
     return 0;
   }
 
  private:
+  static void DumpSummaryStats(std::ostream& os, const HiddenApiStats& stats) {
+    static const char* kPrefix = "       ";
+    os << stats.count << " hidden API(s) used: "
+       << stats.linking_count << " linked against, "
+       << stats.reflection_count << " through reflection" << std::endl;
+    os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kBlacklist]
+       << " in blacklist" << std::endl;
+    os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kDarkGreylist]
+       << " in dark greylist" << std::endl;
+    os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kLightGreylist]
+       << " in light greylist" << std::endl;
+  }
+
   static bool Load(const std::string& filename,
                    std::string& content,
                    std::vector<std::unique_ptr<const DexFile>>* dex_files,
diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h
index 0c928ab..75e4845 100644
--- a/tools/veridex/veridex.h
+++ b/tools/veridex/veridex.h
@@ -25,6 +25,18 @@
 namespace art {
 
 /**
+ * Abstraction for fields defined in dex files. Currently, that's a pointer into their
+ * `encoded_field` description.
+ */
+using VeriField = const uint8_t*;
+
+/**
+ * Abstraction for methods defined in dex files. Currently, that's a pointer into their
+ * `encoded_method` description.
+ */
+using VeriMethod = const uint8_t*;
+
+/**
  * Abstraction for classes defined, or implicitly defined (for arrays and primitives)
  * in dex files.
  */
@@ -52,6 +64,9 @@
   const DexFile::ClassDef* GetClassDef() const { return class_def_; }
 
   static VeriClass* object_;
+  static VeriClass* class_;
+  static VeriClass* string_;
+  static VeriClass* throwable_;
   static VeriClass* boolean_;
   static VeriClass* byte_;
   static VeriClass* char_;
@@ -62,23 +77,26 @@
   static VeriClass* long_;
   static VeriClass* void_;
 
+  static VeriMethod forName_;
+  static VeriMethod getField_;
+  static VeriMethod getDeclaredField_;
+  static VeriMethod getMethod_;
+  static VeriMethod getDeclaredMethod_;
+  static VeriMethod getClass_;
+
  private:
   Primitive::Type kind_;
   uint8_t dimensions_;
   const DexFile::ClassDef* class_def_;
 };
 
-/**
- * Abstraction for fields defined in dex files. Currently, that's a pointer into their
- * `encoded_field` description.
- */
-using VeriField = const uint8_t*;
+inline bool IsGetMethod(VeriMethod method) {
+  return method == VeriClass::getMethod_ || method == VeriClass::getDeclaredMethod_;
+}
 
-/**
- * Abstraction for methods defined in dex files. Currently, that's a pointer into their
- * `encoded_method` description.
- */
-using VeriMethod = const uint8_t*;
+inline bool IsGetField(VeriMethod method) {
+  return method == VeriClass::getField_ || method == VeriClass::getDeclaredField_;
+}
 
 /**
  * Map from name to VeriClass to quickly lookup classes.