Merge "Move some helper methods to DexRegisterLocation."
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index eea146e..2589869 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1055,7 +1055,8 @@
 
 void CodeGenerator::RecordPcInfo(HInstruction* instruction,
                                  uint32_t dex_pc,
-                                 SlowPathCode* slow_path) {
+                                 SlowPathCode* slow_path,
+                                 bool native_debug_info) {
   if (instruction != nullptr) {
     // The code generated for some type conversions
     // may call the runtime, thus normally requiring a subsequent
@@ -1120,12 +1121,23 @@
     outer_dex_pc = outer_environment->GetDexPc();
     outer_environment_size = outer_environment->Size();
   }
+
+  HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+  bool osr =
+      instruction->IsSuspendCheck() &&
+      (info != nullptr) &&
+      graph_->IsCompilingOsr() &&
+      (inlining_depth == 0);
+  StackMap::Kind kind = native_debug_info
+      ? StackMap::Kind::Debug
+      : (osr ? StackMap::Kind::OSR : StackMap::Kind::Default);
   stack_map_stream->BeginStackMapEntry(outer_dex_pc,
                                        native_pc,
                                        register_mask,
                                        locations->GetStackMask(),
                                        outer_environment_size,
-                                       inlining_depth);
+                                       inlining_depth,
+                                       kind);
   EmitEnvironment(environment, slow_path);
   // Record invoke info, the common case for the trampoline is super and static invokes. Only
   // record these to reduce oat file size.
@@ -1138,19 +1150,9 @@
   }
   stack_map_stream->EndStackMapEntry();
 
-  HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
-  if (instruction->IsSuspendCheck() &&
-      (info != nullptr) &&
-      graph_->IsCompilingOsr() &&
-      (inlining_depth == 0)) {
+  if (osr) {
     DCHECK_EQ(info->GetSuspendCheck(), instruction);
-    // We duplicate the stack map as a marker that this stack map can be an OSR entry.
-    // Duplicating it avoids having the runtime recognize and skip an OSR stack map.
     DCHECK(info->IsIrreducible());
-    stack_map_stream->BeginStackMapEntry(
-        dex_pc, native_pc, register_mask, locations->GetStackMask(), outer_environment_size, 0);
-    EmitEnvironment(instruction->GetEnvironment(), slow_path);
-    stack_map_stream->EndStackMapEntry();
     if (kIsDebugBuild) {
       for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) {
         HInstruction* in_environment = environment->GetInstructionAt(i);
@@ -1167,14 +1169,6 @@
         }
       }
     }
-  } else if (kIsDebugBuild) {
-    // Ensure stack maps are unique, by checking that the native pc in the stack map
-    // last emitted is different than the native pc of the stack map just emitted.
-    size_t number_of_stack_maps = stack_map_stream->GetNumberOfStackMaps();
-    if (number_of_stack_maps > 1) {
-      DCHECK_NE(stack_map_stream->GetStackMapNativePcOffset(number_of_stack_maps - 1),
-                stack_map_stream->GetStackMapNativePcOffset(number_of_stack_maps - 2));
-    }
   }
 }
 
@@ -1196,12 +1190,11 @@
       // Ensure that we do not collide with the stack map of the previous instruction.
       GenerateNop();
     }
-    RecordPcInfo(instruction, dex_pc, slow_path);
+    RecordPcInfo(instruction, dex_pc, slow_path, /* native_debug_info */ true);
   }
 }
 
 void CodeGenerator::RecordCatchBlockInfo() {
-  ArenaAllocator* allocator = graph_->GetAllocator();
   StackMapStream* stack_map_stream = GetStackMapStream();
 
   for (HBasicBlock* block : *block_order_) {
@@ -1213,28 +1206,24 @@
     uint32_t num_vregs = graph_->GetNumberOfVRegs();
     uint32_t inlining_depth = 0;  // Inlining of catch blocks is not supported at the moment.
     uint32_t native_pc = GetAddressOf(block);
-    uint32_t register_mask = 0;   // Not used.
-
-    // The stack mask is not used, so we leave it empty.
-    ArenaBitVector* stack_mask =
-        ArenaBitVector::Create(allocator, 0, /* expandable */ true, kArenaAllocCodeGenerator);
 
     stack_map_stream->BeginStackMapEntry(dex_pc,
                                          native_pc,
-                                         register_mask,
-                                         stack_mask,
+                                         /* register_mask */ 0,
+                                         /* stack_mask */ nullptr,
                                          num_vregs,
-                                         inlining_depth);
+                                         inlining_depth,
+                                         StackMap::Kind::Catch);
 
     HInstruction* current_phi = block->GetFirstPhi();
     for (size_t vreg = 0; vreg < num_vregs; ++vreg) {
-    while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) {
-      HInstruction* next_phi = current_phi->GetNext();
-      DCHECK(next_phi == nullptr ||
-             current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber())
-          << "Phis need to be sorted by vreg number to keep this a linear-time loop.";
-      current_phi = next_phi;
-    }
+      while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) {
+        HInstruction* next_phi = current_phi->GetNext();
+        DCHECK(next_phi == nullptr ||
+               current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber())
+            << "Phis need to be sorted by vreg number to keep this a linear-time loop.";
+        current_phi = next_phi;
+      }
 
       if (current_phi == nullptr || current_phi->AsPhi()->GetRegNumber() != vreg) {
         stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0);
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index b3c29aa..03ae498 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -323,7 +323,10 @@
   }
 
   // Record native to dex mapping for a suspend point.  Required by runtime.
-  void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path = nullptr);
+  void RecordPcInfo(HInstruction* instruction,
+                    uint32_t dex_pc,
+                    SlowPathCode* slow_path = nullptr,
+                    bool native_debug_info = false);
   // Check whether we have already recorded mapping at this PC.
   bool HasStackMapAtCurrentPc();
   // Record extra stack maps if we support native debugging.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 5f0533c..8aa790db 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2459,6 +2459,9 @@
           // all & reg_bits - 1.
           __ Ror(dst, lhs, RegisterFrom(instr->GetLocations()->InAt(1), type));
         }
+      } else if (instr->IsMin() || instr->IsMax()) {
+          __ Cmp(lhs, rhs);
+          __ Csel(dst, lhs, rhs, instr->IsMin() ? lt : gt);
       } else {
         DCHECK(instr->IsXor());
         __ Eor(dst, lhs, rhs);
@@ -2474,6 +2477,10 @@
         __ Fadd(dst, lhs, rhs);
       } else if (instr->IsSub()) {
         __ Fsub(dst, lhs, rhs);
+      } else if (instr->IsMin()) {
+        __ Fmin(dst, lhs, rhs);
+      } else if (instr->IsMax()) {
+        __ Fmax(dst, lhs, rhs);
       } else {
         LOG(FATAL) << "Unexpected floating-point binary operation";
       }
@@ -5671,111 +5678,20 @@
   }
 }
 
-// TODO: integrate with HandleBinaryOp?
-static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
-  LocationSummary* locations = new (allocator) LocationSummary(minmax);
-  switch (minmax->GetResultType()) {
-    case DataType::Type::kInt32:
-    case DataType::Type::kInt64:
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RequiresRegister());
-      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      break;
-    case DataType::Type::kFloat32:
-    case DataType::Type::kFloat64:
-      locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, Location::RequiresFpuRegister());
-      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-      break;
-    default:
-      LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
-  }
-}
-
-void InstructionCodeGeneratorARM64::GenerateMinMaxInt(LocationSummary* locations,
-                                                      bool is_min,
-                                                      DataType::Type type) {
-  Location op1 = locations->InAt(0);
-  Location op2 = locations->InAt(1);
-  Location out = locations->Out();
-
-  Register op1_reg;
-  Register op2_reg;
-  Register out_reg;
-  if (type == DataType::Type::kInt64) {
-    op1_reg = XRegisterFrom(op1);
-    op2_reg = XRegisterFrom(op2);
-    out_reg = XRegisterFrom(out);
-  } else {
-    DCHECK_EQ(type, DataType::Type::kInt32);
-    op1_reg = WRegisterFrom(op1);
-    op2_reg = WRegisterFrom(op2);
-    out_reg = WRegisterFrom(out);
-  }
-
-  __ Cmp(op1_reg, op2_reg);
-  __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
-}
-
-void InstructionCodeGeneratorARM64::GenerateMinMaxFP(LocationSummary* locations,
-                                                     bool is_min,
-                                                     DataType::Type type) {
-  Location op1 = locations->InAt(0);
-  Location op2 = locations->InAt(1);
-  Location out = locations->Out();
-
-  FPRegister op1_reg;
-  FPRegister op2_reg;
-  FPRegister out_reg;
-  if (type == DataType::Type::kFloat64) {
-    op1_reg = DRegisterFrom(op1);
-    op2_reg = DRegisterFrom(op2);
-    out_reg = DRegisterFrom(out);
-  } else {
-    DCHECK_EQ(type, DataType::Type::kFloat32);
-    op1_reg = SRegisterFrom(op1);
-    op2_reg = SRegisterFrom(op2);
-    out_reg = SRegisterFrom(out);
-  }
-
-  if (is_min) {
-    __ Fmin(out_reg, op1_reg, op2_reg);
-  } else {
-    __ Fmax(out_reg, op1_reg, op2_reg);
-  }
-}
-
-// TODO: integrate with HandleBinaryOp?
-void InstructionCodeGeneratorARM64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
-  DataType::Type type = minmax->GetResultType();
-  switch (type) {
-    case DataType::Type::kInt32:
-    case DataType::Type::kInt64:
-      GenerateMinMaxInt(minmax->GetLocations(), is_min, type);
-      break;
-    case DataType::Type::kFloat32:
-    case DataType::Type::kFloat64:
-      GenerateMinMaxFP(minmax->GetLocations(), is_min, type);
-      break;
-    default:
-      LOG(FATAL) << "Unexpected type for HMinMax " << type;
-  }
-}
-
 void LocationsBuilderARM64::VisitMin(HMin* min) {
-  CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
+  HandleBinaryOp(min);
 }
 
 void InstructionCodeGeneratorARM64::VisitMin(HMin* min) {
-  GenerateMinMax(min, /*is_min*/ true);
+  HandleBinaryOp(min);
 }
 
 void LocationsBuilderARM64::VisitMax(HMax* max) {
-  CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
+  HandleBinaryOp(max);
 }
 
 void InstructionCodeGeneratorARM64::VisitMax(HMax* max) {
-  GenerateMinMax(max, /*is_min*/ false);
+  HandleBinaryOp(max);
 }
 
 void LocationsBuilderARM64::VisitAbs(HAbs* abs) {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index e7fe5b7..5afb712 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -280,10 +280,6 @@
   void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
   void HandleCondition(HCondition* instruction);
 
-  void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type);
-  void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type);
-  void GenerateMinMax(HBinaryOperation* minmax, bool is_min);
-
   // Generate a heap reference load using one register `out`:
   //
   //   out <- *(out + offset)
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index ed2f8e9..5191ee2 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -234,6 +234,13 @@
   }
 }
 
+inline bool AddSubCanEncodeAsImmediate(int64_t value) {
+  // If `value` does not fit but `-value` does, VIXL will automatically use
+  // the 'opposite' instruction.
+  return vixl::aarch64::Assembler::IsImmAddSub(value)
+      || vixl::aarch64::Assembler::IsImmAddSub(-value);
+}
+
 inline bool Arm64CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* instr) {
   int64_t value = CodeGenerator::GetInt64ValueOf(constant);
 
@@ -249,6 +256,20 @@
     return IsUint<8>(value);
   }
 
+  // Code generation for Min/Max:
+  //    Cmp left_op, right_op
+  //    Csel dst, left_op, right_op, cond
+  if (instr->IsMin() || instr->IsMax()) {
+    if (constant->GetUses().HasExactlyOneElement()) {
+      // If value can be encoded as immediate for the Cmp, then let VIXL handle
+      // the constant generation for the Csel.
+      return AddSubCanEncodeAsImmediate(value);
+    }
+    // These values are encodable as immediates for Cmp and VIXL will use csinc and csinv
+    // with the zr register as right_op, hence no constant generation is required.
+    return constant->IsZeroBitPattern() || constant->IsOne() || constant->IsMinusOne();
+  }
+
   // For single uses we let VIXL handle the constant generation since it will
   // use registers that are not managed by the register allocator (wip0, wip1).
   if (constant->GetUses().HasExactlyOneElement()) {
@@ -275,10 +296,7 @@
            instr->IsSub())
         << instr->DebugName();
     // Uses aliases of ADD/SUB instructions.
-    // If `value` does not fit but `-value` does, VIXL will automatically use
-    // the 'opposite' instruction.
-    return vixl::aarch64::Assembler::IsImmAddSub(value)
-        || vixl::aarch64::Assembler::IsImmAddSub(-value);
+    return AddSubCanEncodeAsImmediate(value);
   }
 }
 
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index d532eee..c979a5a 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -117,6 +117,7 @@
   void SimplifyFP2Int(HInvoke* invoke);
   void SimplifyStringCharAt(HInvoke* invoke);
   void SimplifyStringIsEmptyOrLength(HInvoke* invoke);
+  void SimplifyStringIndexOf(HInvoke* invoke);
   void SimplifyNPEOnArgN(HInvoke* invoke, size_t);
   void SimplifyReturnThis(HInvoke* invoke);
   void SimplifyAllocationIntrinsic(HInvoke* invoke);
@@ -2417,6 +2418,43 @@
   invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, replacement);
 }
 
+void InstructionSimplifierVisitor::SimplifyStringIndexOf(HInvoke* invoke) {
+  DCHECK(invoke->GetIntrinsic() == Intrinsics::kStringIndexOf ||
+         invoke->GetIntrinsic() == Intrinsics::kStringIndexOfAfter);
+  if (invoke->InputAt(0)->IsLoadString()) {
+    HLoadString* load_string = invoke->InputAt(0)->AsLoadString();
+    const DexFile& dex_file = load_string->GetDexFile();
+    uint32_t utf16_length;
+    const char* data =
+        dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), &utf16_length);
+    if (utf16_length == 0) {
+      invoke->ReplaceWith(GetGraph()->GetIntConstant(-1));
+      invoke->GetBlock()->RemoveInstruction(invoke);
+      RecordSimplification();
+      return;
+    }
+    if (utf16_length == 1 && invoke->GetIntrinsic() == Intrinsics::kStringIndexOf) {
+      // Simplify to HSelect(HEquals(., load_string.charAt(0)), 0, -1).
+      // If the sought character is supplementary, this gives the correct result, i.e. -1.
+      uint32_t c = GetUtf16FromUtf8(&data);
+      DCHECK_EQ(GetTrailingUtf16Char(c), 0u);
+      DCHECK_EQ(GetLeadingUtf16Char(c), c);
+      uint32_t dex_pc = invoke->GetDexPc();
+      ArenaAllocator* allocator = GetGraph()->GetAllocator();
+      HEqual* equal =
+          new (allocator) HEqual(invoke->InputAt(1), GetGraph()->GetIntConstant(c), dex_pc);
+      invoke->GetBlock()->InsertInstructionBefore(equal, invoke);
+      HSelect* result = new (allocator) HSelect(equal,
+                                                GetGraph()->GetIntConstant(0),
+                                                GetGraph()->GetIntConstant(-1),
+                                                dex_pc);
+      invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, result);
+      RecordSimplification();
+      return;
+    }
+  }
+}
+
 // This method should only be used on intrinsics whose sole way of throwing an
 // exception is raising a NPE when the nth argument is null. If that argument
 // is provably non-null, we can clear the flag.
@@ -2554,6 +2592,10 @@
     case Intrinsics::kStringLength:
       SimplifyStringIsEmptyOrLength(instruction);
       break;
+    case Intrinsics::kStringIndexOf:
+    case Intrinsics::kStringIndexOfAfter:
+      SimplifyStringIndexOf(instruction);
+      break;
     case Intrinsics::kStringStringIndexOf:
     case Intrinsics::kStringStringIndexOfAfter:
       SimplifyNPEOnArgN(instruction, 1);  // 0th has own NullCheck
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 2bfa430..58a35dd 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -31,11 +31,12 @@
 constexpr static bool kVerifyStackMaps = kIsDebugBuild;
 
 uint32_t StackMapStream::GetStackMapNativePcOffset(size_t i) {
-  return StackMap::UnpackNativePc(stack_maps_[i].packed_native_pc, instruction_set_);
+  return StackMap::UnpackNativePc(stack_maps_[i][StackMap::kPackedNativePc], instruction_set_);
 }
 
 void StackMapStream::SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) {
-  stack_maps_[i].packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_);
+  stack_maps_[i][StackMap::kPackedNativePc] =
+      StackMap::PackNativePc(native_pc_offset, instruction_set_);
 }
 
 void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
@@ -43,7 +44,8 @@
                                         uint32_t register_mask,
                                         BitVector* stack_mask,
                                         uint32_t num_dex_registers,
-                                        uint8_t inlining_depth) {
+                                        uint8_t inlining_depth,
+                                        StackMap::Kind kind) {
   DCHECK(!in_stack_map_) << "Mismatched Begin/End calls";
   in_stack_map_ = true;
   // num_dex_registers_ is the constant per-method number of registers.
@@ -54,19 +56,17 @@
     DCHECK_EQ(num_dex_registers_, num_dex_registers) << "Inconsistent register count";
   }
 
-  current_stack_map_ = StackMapEntry {
-    .packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_),
-    .dex_pc = dex_pc,
-    .register_mask_index = kNoValue,
-    .stack_mask_index = kNoValue,
-    .inline_info_index = kNoValue,
-    .dex_register_mask_index = kNoValue,
-    .dex_register_map_index = kNoValue,
-  };
+  current_stack_map_ = BitTableBuilder<StackMap::kCount>::Entry();
+  current_stack_map_[StackMap::kKind] = static_cast<uint32_t>(kind);
+  current_stack_map_[StackMap::kPackedNativePc] =
+      StackMap::PackNativePc(native_pc_offset, instruction_set_);
+  current_stack_map_[StackMap::kDexPc] = dex_pc;
   if (register_mask != 0) {
     uint32_t shift = LeastSignificantBit(register_mask);
-    RegisterMaskEntry entry = { register_mask >> shift, shift };
-    current_stack_map_.register_mask_index = register_masks_.Dedup(&entry);
+    BitTableBuilder<RegisterMask::kCount>::Entry entry;
+    entry[RegisterMask::kValue] = register_mask >> shift;
+    entry[RegisterMask::kShift] = shift;
+    current_stack_map_[StackMap::kRegisterMaskIndex] = register_masks_.Dedup(&entry);
   }
   // The compiler assumes the bit vector will be read during PrepareForFillIn(),
   // and it might modify the data before that. Therefore, just store the pointer.
@@ -81,8 +81,17 @@
     // Create lambda method, which will be executed at the very end to verify data.
     // Parameters and local variables will be captured(stored) by the lambda "[=]".
     dchecks_.emplace_back([=](const CodeInfo& code_info) {
+      if (kind == StackMap::Kind::Default || kind == StackMap::Kind::OSR) {
+        StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset,
+                                                                    instruction_set_);
+        CHECK_EQ(stack_map.Row(), stack_map_index);
+      } else if (kind == StackMap::Kind::Catch) {
+        StackMap stack_map = code_info.GetCatchStackMapForDexPc(dex_pc);
+        CHECK_EQ(stack_map.Row(), stack_map_index);
+      }
       StackMap stack_map = code_info.GetStackMapAt(stack_map_index);
       CHECK_EQ(stack_map.GetNativePcOffset(instruction_set_), native_pc_offset);
+      CHECK_EQ(stack_map.GetKind(), static_cast<uint32_t>(kind));
       CHECK_EQ(stack_map.GetDexPc(), dex_pc);
       CHECK_EQ(code_info.GetRegisterMaskOf(stack_map), register_mask);
       BitMemoryRegion seen_stack_mask = code_info.GetStackMaskOf(stack_map);
@@ -103,8 +112,8 @@
 
   // Generate index into the InlineInfo table.
   if (!current_inline_infos_.empty()) {
-    current_inline_infos_.back().is_last = InlineInfo::kLast;
-    current_stack_map_.inline_info_index =
+    current_inline_infos_.back()[InlineInfo::kIsLast] = InlineInfo::kLast;
+    current_stack_map_[StackMap::kInlineInfoIndex] =
         inline_infos_.Dedup(current_inline_infos_.data(), current_inline_infos_.size());
   }
 
@@ -115,13 +124,13 @@
 }
 
 void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) {
-  uint32_t packed_native_pc = current_stack_map_.packed_native_pc;
+  uint32_t packed_native_pc = current_stack_map_[StackMap::kPackedNativePc];
   size_t invoke_info_index = invoke_infos_.size();
-  invoke_infos_.Add(InvokeInfoEntry {
-    .packed_native_pc = packed_native_pc,
-    .invoke_type = invoke_type,
-    .method_info_index = method_infos_.Dedup(&dex_method_index),
-  });
+  BitTableBuilder<InvokeInfo::kCount>::Entry entry;
+  entry[InvokeInfo::kPackedNativePc] = packed_native_pc;
+  entry[InvokeInfo::kInvokeType] = invoke_type;
+  entry[InvokeInfo::kMethodInfoIndex] = method_infos_.Dedup({dex_method_index});
+  invoke_infos_.Add(entry);
 
   if (kVerifyStackMaps) {
     dchecks_.emplace_back([=](const CodeInfo& code_info) {
@@ -129,7 +138,7 @@
       CHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_),
                StackMap::UnpackNativePc(packed_native_pc, instruction_set_));
       CHECK_EQ(invoke_info.GetInvokeType(), invoke_type);
-      CHECK_EQ(method_infos_[invoke_info.GetMethodInfoIndex()], dex_method_index);
+      CHECK_EQ(method_infos_[invoke_info.GetMethodInfoIndex()][0], dex_method_index);
     });
   }
 }
@@ -144,24 +153,20 @@
 
   expected_num_dex_registers_ += num_dex_registers;
 
-  InlineInfoEntry entry = {
-    .is_last = InlineInfo::kMore,
-    .dex_pc = dex_pc,
-    .method_info_index = kNoValue,
-    .art_method_hi = kNoValue,
-    .art_method_lo = kNoValue,
-    .num_dex_registers = static_cast<uint32_t>(expected_num_dex_registers_),
-  };
+  BitTableBuilder<InlineInfo::kCount>::Entry entry;
+  entry[InlineInfo::kIsLast] = InlineInfo::kMore;
+  entry[InlineInfo::kDexPc] = dex_pc;
+  entry[InlineInfo::kNumberOfDexRegisters] = static_cast<uint32_t>(expected_num_dex_registers_);
   if (EncodeArtMethodInInlineInfo(method)) {
-    entry.art_method_hi = High32Bits(reinterpret_cast<uintptr_t>(method));
-    entry.art_method_lo = Low32Bits(reinterpret_cast<uintptr_t>(method));
+    entry[InlineInfo::kArtMethodHi] = High32Bits(reinterpret_cast<uintptr_t>(method));
+    entry[InlineInfo::kArtMethodLo] = Low32Bits(reinterpret_cast<uintptr_t>(method));
   } else {
     if (dex_pc != static_cast<uint32_t>(-1) && kIsDebugBuild) {
       ScopedObjectAccess soa(Thread::Current());
       DCHECK(IsSameDexFile(*outer_dex_file, *method->GetDexFile()));
     }
     uint32_t dex_method_index = method->GetDexMethodIndexUnchecked();
-    entry.method_info_index = method_infos_.Dedup(&dex_method_index);
+    entry[InlineInfo::kMethodInfoIndex] = method_infos_.Dedup({dex_method_index});
   }
   current_inline_infos_.push_back(entry);
 
@@ -177,7 +182,7 @@
       if (encode_art_method) {
         CHECK_EQ(inline_info.GetArtMethod(), method);
       } else {
-        CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()],
+        CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()][0],
                  method->GetDexMethodIndexUnchecked());
       }
     });
@@ -210,13 +215,13 @@
     // Distance is difference between this index and the index of last modification.
     uint32_t distance = stack_maps_.size() - dex_register_timestamp_[i];
     if (previous_dex_registers_[i] != reg || distance > kMaxDexRegisterMapSearchDistance) {
-      DexRegisterEntry entry = DexRegisterEntry{
-        .kind = static_cast<uint32_t>(reg.GetKind()),
-        .packed_value = DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue()),
-      };
+      BitTableBuilder<DexRegisterInfo::kCount>::Entry entry;
+      entry[DexRegisterInfo::kKind] = static_cast<uint32_t>(reg.GetKind());
+      entry[DexRegisterInfo::kPackedValue] =
+          DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue());
       uint32_t index = reg.IsLive() ? dex_register_catalog_.Dedup(&entry) : kNoValue;
       temp_dex_register_mask_.SetBit(i);
-      temp_dex_register_map_.push_back(index);
+      temp_dex_register_map_.push_back({index});
       previous_dex_registers_[i] = reg;
       dex_register_timestamp_[i] = stack_maps_.size();
     }
@@ -224,12 +229,12 @@
 
   // Set the mask and map for the current StackMap (which includes inlined registers).
   if (temp_dex_register_mask_.GetNumberOfBits() != 0) {
-    current_stack_map_.dex_register_mask_index =
+    current_stack_map_[StackMap::kDexRegisterMaskIndex] =
         dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(),
                                   temp_dex_register_mask_.GetNumberOfBits());
   }
   if (!current_dex_registers_.empty()) {
-    current_stack_map_.dex_register_map_index =
+    current_stack_map_[StackMap::kDexRegisterMapIndex] =
         dex_register_maps_.Dedup(temp_dex_register_map_.data(),
                                  temp_dex_register_map_.size());
   }
@@ -260,7 +265,7 @@
   {
     MethodInfo info(region.begin(), method_infos_.size());
     for (size_t i = 0; i < method_infos_.size(); ++i) {
-      info.SetMethodIndex(i, method_infos_[i]);
+      info.SetMethodIndex(i, method_infos_[i][0]);
     }
   }
   if (kVerifyStackMaps) {
@@ -269,23 +274,19 @@
     const size_t count = info.NumMethodIndices();
     DCHECK_EQ(count, method_infos_.size());
     for (size_t i = 0; i < count; ++i) {
-      DCHECK_EQ(info.GetMethodIndex(i), method_infos_[i]);
+      DCHECK_EQ(info.GetMethodIndex(i), method_infos_[i][0]);
     }
   }
 }
 
 size_t StackMapStream::PrepareForFillIn() {
-  static_assert(sizeof(StackMapEntry) == StackMap::kCount * sizeof(uint32_t), "Layout");
-  static_assert(sizeof(InvokeInfoEntry) == InvokeInfo::kCount * sizeof(uint32_t), "Layout");
-  static_assert(sizeof(InlineInfoEntry) == InlineInfo::kCount * sizeof(uint32_t), "Layout");
-  static_assert(sizeof(DexRegisterEntry) == DexRegisterInfo::kCount * sizeof(uint32_t), "Layout");
   DCHECK_EQ(out_.size(), 0u);
 
   // Read the stack masks now. The compiler might have updated them.
   for (size_t i = 0; i < lazy_stack_masks_.size(); i++) {
     BitVector* stack_mask = lazy_stack_masks_[i];
     if (stack_mask != nullptr && stack_mask->GetNumberOfBits() != 0) {
-      stack_maps_[i].stack_mask_index =
+      stack_maps_[i][StackMap::kStackMaskIndex] =
         stack_masks_.Dedup(stack_mask->GetRawStorage(), stack_mask->GetNumberOfBits());
     }
   }
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index e3ae8a2..6842d9f 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -27,11 +27,10 @@
 #include "dex_register_location.h"
 #include "method_info.h"
 #include "nodes.h"
+#include "stack_map.h"
 
 namespace art {
 
-class CodeInfo;
-
 /**
  * Collects and builds stack maps for a method. All the stack maps
  * for a method are placed in a CodeInfo object.
@@ -53,6 +52,7 @@
         lazy_stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)),
         in_stack_map_(false),
         in_inline_info_(false),
+        current_stack_map_(),
         current_inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)),
         current_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)),
         previous_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)),
@@ -66,7 +66,8 @@
                           uint32_t register_mask,
                           BitVector* sp_mask,
                           uint32_t num_dex_registers,
-                          uint8_t inlining_depth);
+                          uint8_t inlining_depth,
+                          StackMap::Kind kind = StackMap::Kind::Default);
   void EndStackMapEntry();
 
   void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) {
@@ -99,69 +100,29 @@
  private:
   static constexpr uint32_t kNoValue = -1;
 
-  // The fields must be uint32_t and mirror the StackMap accessor in stack_map.h!
-  struct StackMapEntry {
-    uint32_t packed_native_pc;
-    uint32_t dex_pc;
-    uint32_t register_mask_index;
-    uint32_t stack_mask_index;
-    uint32_t inline_info_index;
-    uint32_t dex_register_mask_index;
-    uint32_t dex_register_map_index;
-  };
-
-  // The fields must be uint32_t and mirror the InlineInfo accessor in stack_map.h!
-  struct InlineInfoEntry {
-    uint32_t is_last;
-    uint32_t dex_pc;
-    uint32_t method_info_index;
-    uint32_t art_method_hi;
-    uint32_t art_method_lo;
-    uint32_t num_dex_registers;
-  };
-
-  // The fields must be uint32_t and mirror the InvokeInfo accessor in stack_map.h!
-  struct InvokeInfoEntry {
-    uint32_t packed_native_pc;
-    uint32_t invoke_type;
-    uint32_t method_info_index;
-  };
-
-  // The fields must be uint32_t and mirror the DexRegisterInfo accessor in stack_map.h!
-  struct DexRegisterEntry {
-    uint32_t kind;
-    uint32_t packed_value;
-  };
-
-  // The fields must be uint32_t and mirror the RegisterMask accessor in stack_map.h!
-  struct RegisterMaskEntry {
-    uint32_t value;
-    uint32_t shift;
-  };
-
   void CreateDexRegisterMap();
 
   const InstructionSet instruction_set_;
-  BitTableBuilder<StackMapEntry> stack_maps_;
-  BitTableBuilder<RegisterMaskEntry> register_masks_;
+  BitTableBuilder<StackMap::kCount> stack_maps_;
+  BitTableBuilder<RegisterMask::kCount> register_masks_;
   BitmapTableBuilder stack_masks_;
-  BitTableBuilder<InvokeInfoEntry> invoke_infos_;
-  BitTableBuilder<InlineInfoEntry> inline_infos_;
+  BitTableBuilder<InvokeInfo::kCount> invoke_infos_;
+  BitTableBuilder<InlineInfo::kCount> inline_infos_;
   BitmapTableBuilder dex_register_masks_;
-  BitTableBuilder<uint32_t> dex_register_maps_;
-  BitTableBuilder<DexRegisterEntry> dex_register_catalog_;
+  BitTableBuilder<MaskInfo::kCount> dex_register_maps_;
+  BitTableBuilder<DexRegisterInfo::kCount> dex_register_catalog_;
   uint32_t num_dex_registers_ = 0;  // TODO: Make this const and get the value in constructor.
   ScopedArenaVector<uint8_t> out_;
 
-  BitTableBuilder<uint32_t> method_infos_;
+  BitTableBuilder<1> method_infos_;
 
   ScopedArenaVector<BitVector*> lazy_stack_masks_;
 
   // Variables which track the current state between Begin/End calls;
   bool in_stack_map_;
   bool in_inline_info_;
-  StackMapEntry current_stack_map_;
-  ScopedArenaVector<InlineInfoEntry> current_inline_infos_;
+  BitTableBuilder<StackMap::kCount>::Entry current_stack_map_;
+  ScopedArenaVector<BitTableBuilder<InlineInfo::kCount>::Entry> current_inline_infos_;
   ScopedArenaVector<DexRegisterLocation> current_dex_registers_;
   ScopedArenaVector<DexRegisterLocation> previous_dex_registers_;
   ScopedArenaVector<uint32_t> dex_register_timestamp_;  // Stack map index of last change.
@@ -170,7 +131,7 @@
   // Temporary variables used in CreateDexRegisterMap.
   // They are here so that we can reuse the reserved memory.
   ArenaBitVector temp_dex_register_mask_;
-  ScopedArenaVector<uint32_t> temp_dex_register_map_;
+  ScopedArenaVector<BitTableBuilder<DexRegisterMapInfo::kCount>::Entry> temp_dex_register_map_;
 
   // A set of lambda functions to be executed at the end to verify
   // the encoded data. It is generally only used in debug builds.
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 56717b5..fd85667 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -425,12 +425,12 @@
   stream.AddDexRegisterEntry(Kind::kConstant, -2);   // Large location.
   stream.EndStackMapEntry();
   // Second stack map, which should share the same dex register map.
-  stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0);
+  stream.BeginStackMapEntry(0, 65 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0);
   stream.AddDexRegisterEntry(Kind::kInRegister, 0);  // Short location.
   stream.AddDexRegisterEntry(Kind::kConstant, -2);   // Large location.
   stream.EndStackMapEntry();
   // Third stack map (doesn't share the dex register map).
-  stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0);
+  stream.BeginStackMapEntry(0, 66 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0);
   stream.AddDexRegisterEntry(Kind::kInRegister, 2);  // Short location.
   stream.AddDexRegisterEntry(Kind::kConstant, -2);   // Large location.
   stream.EndStackMapEntry();
@@ -528,7 +528,7 @@
   sp_mask1.SetBit(4);
 
   // First stack map.
-  stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask1, 2, 2);
+  stream.BeginStackMapEntry(0, 10 * kPcAlign, 0x3, &sp_mask1, 2, 2);
   stream.AddDexRegisterEntry(Kind::kInStack, 0);
   stream.AddDexRegisterEntry(Kind::kConstant, 4);
 
diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc
index 2b4144c..3f5dbcf 100644
--- a/dexlayout/compact_dex_writer.cc
+++ b/dexlayout/compact_dex_writer.cc
@@ -59,8 +59,8 @@
       for (auto& method : *(invoke_type == InvokeType::kDirect
                                 ? class_data->DirectMethods()
                                 : class_data->VirtualMethods())) {
-        const dex_ir::MethodId* method_id = method->GetMethodId();
-        dex_ir::CodeItem* code_item = method->GetCodeItem();
+        const dex_ir::MethodId* method_id = method.GetMethodId();
+        dex_ir::CodeItem* code_item = method.GetCodeItem();
         if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
           const uint32_t debug_info_offset = code_item->DebugInfo()->GetOffset();
           const uint32_t method_idx = method_id->GetIndex();
@@ -248,8 +248,8 @@
       for (auto& method : *(invoke_type == InvokeType::kDirect
                                 ? class_data->DirectMethods()
                                 : class_data->VirtualMethods())) {
-        const dex_ir::MethodId* method_id = method->GetMethodId();
-        dex_ir::CodeItem* code_item = method->GetCodeItem();
+        const dex_ir::MethodId* method_id = method.GetMethodId();
+        dex_ir::CodeItem* code_item = method.GetCodeItem();
         if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
           const dex_ir::DebugInfoItem* debug_item = code_item->DebugInfo();
           method_idx_map.insert(std::make_pair(debug_item, method_id->GetIndex()));
@@ -350,8 +350,8 @@
       for (auto& method : *(invoke_type == InvokeType::kDirect
                                 ? class_data->DirectMethods()
                                 : class_data->VirtualMethods())) {
-        const uint32_t idx = method->GetMethodId()->GetIndex();
-        dex_ir::CodeItem* code_item = method->GetCodeItem();
+        const uint32_t idx = method.GetMethodId()->GetIndex();
+        dex_ir::CodeItem* code_item = method.GetCodeItem();
         dex_ir:: DebugInfoItem* debug_info_item = nullptr;
         if (code_item != nullptr) {
           debug_info_item = code_item->DebugInfo();
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index b7d9db6..15e3baf 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -318,17 +318,22 @@
 
 void Collections::CreateStringId(const DexFile& dex_file, uint32_t i) {
   const DexFile::StringId& disk_string_id = dex_file.GetStringId(dex::StringIndex(i));
-  StringData* string_data = new StringData(dex_file.GetStringData(disk_string_id));
-  AddItem(string_datas_map_, string_datas_, string_data, disk_string_id.string_data_off_);
-
-  StringId* string_id = new StringId(string_data);
-  AddIndexedItem(string_ids_, string_id, StringIdsOffset() + i * StringId::ItemSize(), i);
+  StringData* string_data = CreateAndAddItem(string_datas_map_,
+                                             string_datas_,
+                                             disk_string_id.string_data_off_,
+                                             dex_file.GetStringData(disk_string_id));
+  CreateAndAddIndexedItem(string_ids_,
+                          StringIdsOffset() + i * StringId::ItemSize(),
+                          i,
+                          string_data);
 }
 
 void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) {
   const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(dex::TypeIndex(i));
-  TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_.index_));
-  AddIndexedItem(type_ids_, type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i);
+  CreateAndAddIndexedItem(type_ids_,
+                          TypeIdsOffset() + i * TypeId::ItemSize(),
+                          i,
+                          GetStringId(disk_type_id.descriptor_idx_.index_));
 }
 
 void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) {
@@ -336,26 +341,32 @@
   const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id);
   TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_);
 
-  ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_.index_),
-                                  GetTypeId(disk_proto_id.return_type_idx_.index_),
-                                  parameter_type_list);
-  AddIndexedItem(proto_ids_, proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i);
+  CreateAndAddIndexedItem(proto_ids_,
+                          ProtoIdsOffset() + i * ProtoId::ItemSize(),
+                          i,
+                          GetStringId(disk_proto_id.shorty_idx_.index_),
+                          GetTypeId(disk_proto_id.return_type_idx_.index_),
+                          parameter_type_list);
 }
 
 void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) {
   const DexFile::FieldId& disk_field_id = dex_file.GetFieldId(i);
-  FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_.index_),
-                                  GetTypeId(disk_field_id.type_idx_.index_),
-                                  GetStringId(disk_field_id.name_idx_.index_));
-  AddIndexedItem(field_ids_, field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i);
+  CreateAndAddIndexedItem(field_ids_,
+                          FieldIdsOffset() + i * FieldId::ItemSize(),
+                          i,
+                          GetTypeId(disk_field_id.class_idx_.index_),
+                          GetTypeId(disk_field_id.type_idx_.index_),
+                          GetStringId(disk_field_id.name_idx_.index_));
 }
 
 void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) {
   const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i);
-  MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_),
-                                     GetProtoId(disk_method_id.proto_idx_.index_),
-                                     GetStringId(disk_method_id.name_idx_.index_));
-  AddIndexedItem(method_ids_, method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i);
+  CreateAndAddIndexedItem(method_ids_,
+                          MethodIdsOffset() + i * MethodId::ItemSize(),
+                          i,
+                          GetTypeId(disk_method_id.class_idx_.index_),
+                          GetProtoId(disk_method_id.proto_idx_.index_),
+                          GetStringId(disk_method_id.name_idx_.index_));
 }
 
 void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) {
@@ -382,9 +393,17 @@
       CreateEncodedArrayItem(dex_file, static_data, disk_class_def.static_values_off_);
   ClassData* class_data = CreateClassData(
       dex_file, dex_file.GetClassData(disk_class_def), disk_class_def.class_data_off_);
-  ClassDef* class_def = new ClassDef(class_type, access_flags, superclass, interfaces_type_list,
-                                     source_file, annotations, static_values, class_data);
-  AddIndexedItem(class_defs_, class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i);
+  CreateAndAddIndexedItem(class_defs_,
+                          ClassDefsOffset() + i * ClassDef::ItemSize(),
+                          i,
+                          class_type,
+                          access_flags,
+                          superclass,
+                          interfaces_type_list,
+                          source_file,
+                          annotations,
+                          static_values,
+                          class_data);
 }
 
 TypeList* Collections::CreateTypeList(const DexFile::TypeList* dex_type_list, uint32_t offset) {
@@ -398,8 +417,7 @@
     for (uint32_t index = 0; index < size; ++index) {
       type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_.index_));
     }
-    type_list = new TypeList(type_vector);
-    AddItem(type_lists_map_, type_lists_, type_list, offset);
+    type_list = CreateAndAddItem(type_lists_map_, type_lists_, offset, type_vector);
   }
   return type_list;
 }
@@ -418,8 +436,10 @@
       values->push_back(std::unique_ptr<EncodedValue>(ReadEncodedValue(dex_file, &static_data)));
     }
     // TODO: Calculate the size of the encoded array.
-    encoded_array_item = new EncodedArrayItem(values);
-    AddItem(encoded_array_items_map_, encoded_array_items_, encoded_array_item, offset);
+    encoded_array_item = CreateAndAddItem(encoded_array_items_map_,
+                                          encoded_array_items_,
+                                          offset,
+                                          values);
   }
   return encoded_array_item;
 }
@@ -447,9 +467,12 @@
     const uint8_t* annotation_data = annotation->annotation_;
     std::unique_ptr<EncodedValue> encoded_value(
         ReadEncodedValue(dex_file, &annotation_data, DexFile::kDexAnnotationAnnotation, 0));
-    annotation_item = new AnnotationItem(visibility, encoded_value->ReleaseEncodedAnnotation());
+    annotation_item = CreateAndAddItem(annotation_items_map_,
+                                       annotation_items_,
+                                       offset,
+                                       visibility,
+                                       encoded_value->ReleaseEncodedAnnotation());
     annotation_item->SetSize(annotation_data - start_data);
-    AddItem(annotation_items_map_, annotation_items_, annotation_item, offset);
   }
   return annotation_item;
 }
@@ -472,8 +495,10 @@
       AnnotationItem* annotation_item = CreateAnnotationItem(dex_file, annotation);
       items->push_back(annotation_item);
     }
-    annotation_set_item = new AnnotationSetItem(items);
-    AddItem(annotation_set_items_map_, annotation_set_items_, annotation_set_item, offset);
+    annotation_set_item = CreateAndAddItem(annotation_set_items_map_,
+                                           annotation_set_items_,
+                                           offset,
+                                           items);
   }
   return annotation_set_item;
 }
@@ -538,13 +563,13 @@
     }
   }
   // TODO: Calculate the size of the annotations directory.
-annotations_directory_item = new AnnotationsDirectoryItem(
-      class_annotation, field_annotations, method_annotations, parameter_annotations);
-  AddItem(annotations_directory_items_map_,
-          annotations_directory_items_,
-          annotations_directory_item,
-          offset);
-  return annotations_directory_item;
+  return CreateAndAddItem(annotations_directory_items_map_,
+                          annotations_directory_items_,
+                          offset,
+                          class_annotation,
+                          field_annotations,
+                          method_annotations,
+                          parameter_annotations);
 }
 
 ParameterAnnotation* Collections::GenerateParameterAnnotation(
@@ -559,8 +584,10 @@
       uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_;
       annotations->push_back(CreateAnnotationSetItem(dex_file, annotation_set_item, set_offset));
     }
-    set_ref_list = new AnnotationSetRefList(annotations);
-    AddItem(annotation_set_ref_lists_map_, annotation_set_ref_lists_, set_ref_list, offset);
+    set_ref_list = CreateAndAddItem(annotation_set_ref_lists_map_,
+                                    annotation_set_ref_lists_,
+                                    offset,
+                                    annotations);
   }
   return new ParameterAnnotation(method_id, set_ref_list);
 }
@@ -590,8 +617,11 @@
       uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream);
       uint8_t* debug_info_buffer = new uint8_t[debug_info_size];
       memcpy(debug_info_buffer, debug_info_stream, debug_info_size);
-      debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer);
-      AddItem(debug_info_items_map_, debug_info_items_, debug_info, debug_info_offset);
+      debug_info = CreateAndAddItem(debug_info_items_map_,
+                                    debug_info_items_,
+                                    debug_info_offset,
+                                    debug_info_size,
+                                    debug_info_buffer);
     }
   }
 
@@ -677,14 +707,14 @@
   }
 
   uint32_t size = dex_file.GetCodeItemSize(*disk_code_item);
-  CodeItem* code_item = new CodeItem(accessor.RegistersSize(),
-                                     accessor.InsSize(),
-                                     accessor.OutsSize(),
-                                     debug_info,
-                                     insns_size,
-                                     insns,
-                                     tries,
-                                     handler_list);
+  CodeItem* code_item = code_items_.CreateAndAddItem(accessor.RegistersSize(),
+                                                     accessor.InsSize(),
+                                                     accessor.OutsSize(),
+                                                     debug_info,
+                                                     insns_size,
+                                                     insns,
+                                                     tries,
+                                                     handler_list);
   code_item->SetSize(size);
 
   // Add the code item to the map.
@@ -693,7 +723,6 @@
     code_item->SetOffset(offset);
   }
   code_items_map_.emplace(offsets_pair, code_item);
-  code_items_.AddItem(code_item);
 
   // Add "fixup" references to types, strings, methods, and fields.
   // This is temporary, as we will probably want more detailed parsing of the
@@ -718,7 +747,7 @@
   return code_item;
 }
 
-MethodItem* Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii) {
+MethodItem Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii) {
   MethodId* method_id = GetMethodId(cdii.GetMemberIndex());
   uint32_t access_flags = cdii.GetRawMemberAccessFlags();
   const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
@@ -728,7 +757,7 @@
                                                disk_code_item,
                                                cdii.GetMethodCodeItemOffset(),
                                                cdii.GetMemberIndex());
-  return new MethodItem(access_flags, method_id, code_item);
+  return MethodItem(access_flags, method_id, code_item);
 }
 
 ClassData* Collections::CreateClassData(
@@ -743,29 +772,33 @@
     for (; cdii.HasNextStaticField(); cdii.Next()) {
       FieldId* field_item = GetFieldId(cdii.GetMemberIndex());
       uint32_t access_flags = cdii.GetRawMemberAccessFlags();
-      static_fields->push_back(std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
+      static_fields->emplace_back(access_flags, field_item);
     }
     // Instance fields.
     FieldItemVector* instance_fields = new FieldItemVector();
     for (; cdii.HasNextInstanceField(); cdii.Next()) {
       FieldId* field_item = GetFieldId(cdii.GetMemberIndex());
       uint32_t access_flags = cdii.GetRawMemberAccessFlags();
-      instance_fields->push_back(
-          std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
+      instance_fields->emplace_back(access_flags, field_item);
     }
     // Direct methods.
     MethodItemVector* direct_methods = new MethodItemVector();
     for (; cdii.HasNextDirectMethod(); cdii.Next()) {
-      direct_methods->push_back(std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
+      direct_methods->push_back(GenerateMethodItem(dex_file, cdii));
     }
     // Virtual methods.
     MethodItemVector* virtual_methods = new MethodItemVector();
     for (; cdii.HasNextVirtualMethod(); cdii.Next()) {
-      virtual_methods->push_back(std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
+      virtual_methods->push_back(GenerateMethodItem(dex_file, cdii));
     }
-    class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods);
+    class_data = CreateAndAddItem(class_datas_map_,
+                                  class_datas_,
+                                  offset,
+                                  static_fields,
+                                  instance_fields,
+                                  direct_methods,
+                                  virtual_methods);
     class_data->SetSize(cdii.EndDataPointer() - encoded_data);
-    AddItem(class_datas_map_, class_datas_, class_data, offset);
   }
   return class_data;
 }
@@ -802,8 +835,10 @@
   EncodedArrayItem* call_site_item =
       CreateEncodedArrayItem(dex_file, disk_call_item_ptr, disk_call_site_id.data_off_);
 
-  CallSiteId* call_site_id = new CallSiteId(call_site_item);
-  AddIndexedItem(call_site_ids_, call_site_id, CallSiteIdsOffset() + i * CallSiteId::ItemSize(), i);
+  CreateAndAddIndexedItem(call_site_ids_,
+                          CallSiteIdsOffset() + i * CallSiteId::ItemSize(),
+                          i,
+                          call_site_item);
 }
 
 void Collections::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) {
@@ -824,11 +859,11 @@
   } else {
     field_or_method_id = GetFieldId(index);
   }
-  MethodHandleItem* method_handle = new MethodHandleItem(type, field_or_method_id);
-  AddIndexedItem(method_handle_items_,
-                 method_handle,
-                 MethodHandleItemsOffset() + i * MethodHandleItem::ItemSize(),
-                 i);
+  CreateAndAddIndexedItem(method_handle_items_,
+                          MethodHandleItemsOffset() + i * MethodHandleItem::ItemSize(),
+                          i,
+                          type,
+                          field_or_method_id);
 }
 
 void Collections::SortVectorsByMapOrder() {
@@ -844,6 +879,19 @@
   class_datas_.SortByMapOrder(class_datas_map_.Collection());
 }
 
+void Collections::ClearMaps() {
+  string_datas_map_.Collection().clear();
+  type_lists_map_.Collection().clear();
+  encoded_array_items_map_.Collection().clear();
+  annotation_items_map_.Collection().clear();
+  annotation_set_items_map_.Collection().clear();
+  annotation_set_ref_lists_map_.Collection().clear();
+  annotations_directory_items_map_.Collection().clear();
+  debug_info_items_map_.Collection().clear();
+  code_items_map_.clear();
+  class_datas_map_.Collection().clear();
+}
+
 static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
   return 0;
 }
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 5ecad2b..54ff105 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -129,7 +129,11 @@
 template<class T> class CollectionVector : public CollectionBase<T> {
  public:
   using Vector = std::vector<std::unique_ptr<T>>;
-  CollectionVector() = default;
+  CollectionVector() { }
+  explicit CollectionVector(size_t size) {
+    // Preallocate so that assignment does not invalidate pointers into the vector.
+    collection_.reserve(size);
+  }
 
   uint32_t Size() const { return collection_.size(); }
   Vector& Collection() { return collection_; }
@@ -152,8 +156,11 @@
  protected:
   Vector collection_;
 
-  void AddItem(T* object) {
+  template<class... Args>
+  T* CreateAndAddItem(Args&&... args) {
+    T* object = new T(std::forward<Args>(args)...);
     collection_.push_back(std::unique_ptr<T>(object));
+    return object;
   }
 
  private:
@@ -165,11 +172,20 @@
  public:
   using Vector = std::vector<std::unique_ptr<T>>;
   IndexedCollectionVector() = default;
+  explicit IndexedCollectionVector(size_t size) : CollectionVector<T>(size) { }
 
  private:
-  void AddIndexedItem(T* object, uint32_t index) {
+  template <class... Args>
+  T* CreateAndAddIndexedItem(uint32_t index, Args&&... args) {
+    T* object = CollectionVector<T>::CreateAndAddItem(std::forward<Args>(args)...);
     object->SetIndex(index);
-    CollectionVector<T>::collection_.push_back(std::unique_ptr<T>(object));
+    return object;
+  }
+
+  T* GetElement(uint32_t index) {
+    DCHECK_LT(index, CollectionVector<T>::Size());
+    DCHECK_NE(CollectionVector<T>::collection_[index].get(), static_cast<T*>(nullptr));
+    return CollectionVector<T>::collection_[index].get();
   }
 
   friend class Collections;
@@ -193,6 +209,8 @@
  private:
   std::map<uint32_t, T*> collection_;
 
+  // CollectionMaps do not own the objects they contain, therefore AddItem is supported
+  // rather than CreateAndAddItem.
   void AddItem(T* object, uint32_t offset) {
     auto it = collection_.emplace(offset, object);
     CHECK(it.second) << "CollectionMap already has an object with offset " << offset << " "
@@ -206,13 +224,25 @@
 class Collections {
  public:
   Collections() = default;
+  Collections(uint32_t num_string_ids,
+              uint32_t num_type_ids,
+              uint32_t num_proto_ids,
+              uint32_t num_field_ids,
+              uint32_t num_method_ids,
+              uint32_t num_class_defs)
+      : string_ids_(num_string_ids),
+        type_ids_(num_type_ids),
+        proto_ids_(num_proto_ids),
+        field_ids_(num_field_ids),
+        method_ids_(num_method_ids),
+        class_defs_(num_class_defs) { }
 
-  CollectionVector<StringId>::Vector& StringIds() { return string_ids_.Collection(); }
-  CollectionVector<TypeId>::Vector& TypeIds() { return type_ids_.Collection(); }
-  CollectionVector<ProtoId>::Vector& ProtoIds() { return proto_ids_.Collection(); }
-  CollectionVector<FieldId>::Vector& FieldIds() { return field_ids_.Collection(); }
-  CollectionVector<MethodId>::Vector& MethodIds() { return method_ids_.Collection(); }
-  CollectionVector<ClassDef>::Vector& ClassDefs() { return class_defs_.Collection(); }
+  IndexedCollectionVector<StringId>::Vector& StringIds() { return string_ids_.Collection(); }
+  IndexedCollectionVector<TypeId>::Vector& TypeIds() { return type_ids_.Collection(); }
+  IndexedCollectionVector<ProtoId>::Vector& ProtoIds() { return proto_ids_.Collection(); }
+  IndexedCollectionVector<FieldId>::Vector& FieldIds() { return field_ids_.Collection(); }
+  IndexedCollectionVector<MethodId>::Vector& MethodIds() { return method_ids_.Collection(); }
+  IndexedCollectionVector<ClassDef>::Vector& ClassDefs() { return class_defs_.Collection(); }
   CollectionVector<CallSiteId>::Vector& CallSiteIds() { return call_site_ids_.Collection(); }
   CollectionVector<MethodHandleItem>::Vector& MethodHandleItems()
       { return method_handle_items_.Collection(); }
@@ -266,28 +296,22 @@
                                         uint32_t count);
 
   StringId* GetStringId(uint32_t index) {
-    CHECK_LT(index, StringIdsSize());
-    return StringIds()[index].get();
+    return string_ids_.GetElement(index);
   }
   TypeId* GetTypeId(uint32_t index) {
-    CHECK_LT(index, TypeIdsSize());
-    return TypeIds()[index].get();
+    return type_ids_.GetElement(index);
   }
   ProtoId* GetProtoId(uint32_t index) {
-    CHECK_LT(index, ProtoIdsSize());
-    return ProtoIds()[index].get();
+    return proto_ids_.GetElement(index);
   }
   FieldId* GetFieldId(uint32_t index) {
-    CHECK_LT(index, FieldIdsSize());
-    return FieldIds()[index].get();
+    return field_ids_.GetElement(index);
   }
   MethodId* GetMethodId(uint32_t index) {
-    CHECK_LT(index, MethodIdsSize());
-    return MethodIds()[index].get();
+    return method_ids_.GetElement(index);
   }
   ClassDef* GetClassDef(uint32_t index) {
-    CHECK_LT(index, ClassDefsSize());
-    return ClassDefs()[index].get();
+    return class_defs_.GetElement(index);
   }
   CallSiteId* GetCallSiteId(uint32_t index) {
     CHECK_LT(index, CallSiteIdsSize());
@@ -372,31 +396,35 @@
 
   // Sort the vectors buy map order (same order that was used in the input file).
   void SortVectorsByMapOrder();
+  // Empty the maps, which are only used for IR construction.
+  void ClearMaps();
 
-  template <typename Type>
-  void AddItem(CollectionMap<Type>& map,
-               CollectionVector<Type>& vector,
-               Type* item,
-               uint32_t offset) {
+  template <typename Type, class... Args>
+  Type* CreateAndAddItem(CollectionMap<Type>& map,
+                         CollectionVector<Type>& vector,
+                         uint32_t offset,
+                         Args&&... args) {
+    Type* item = vector.CreateAndAddItem(std::forward<Args>(args)...);
     DCHECK(!map.GetExistingObject(offset));
     DCHECK(!item->OffsetAssigned());
     if (eagerly_assign_offsets_) {
       item->SetOffset(offset);
     }
     map.AddItem(item, offset);
-    vector.AddItem(item);
+    return item;
   }
 
-  template <typename Type>
-  void AddIndexedItem(IndexedCollectionVector<Type>& vector,
-                      Type* item,
-                      uint32_t offset,
-                      uint32_t index) {
+  template <typename Type, class... Args>
+  Type* CreateAndAddIndexedItem(IndexedCollectionVector<Type>& vector,
+                                uint32_t offset,
+                                uint32_t index,
+                                Args&&... args) {
+    Type* item = vector.CreateAndAddIndexedItem(index, std::forward<Args>(args)...);
     DCHECK(!item->OffsetAssigned());
     if (eagerly_assign_offsets_) {
       item->SetOffset(offset);
     }
-    vector.AddIndexedItem(item, index);
+    return item;
   }
 
   void SetEagerlyAssignOffsets(bool eagerly_assign_offsets) {
@@ -425,7 +453,7 @@
 
   ParameterAnnotation* GenerateParameterAnnotation(const DexFile& dex_file, MethodId* method_id,
       const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset);
-  MethodItem* GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii);
+  MethodItem GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii);
 
   // Collection vectors own the IR data.
   IndexedCollectionVector<StringId> string_ids_;
@@ -433,6 +461,7 @@
   IndexedCollectionVector<ProtoId> proto_ids_;
   IndexedCollectionVector<FieldId> field_ids_;
   IndexedCollectionVector<MethodId> method_ids_;
+  IndexedCollectionVector<ClassDef> class_defs_;
   IndexedCollectionVector<CallSiteId> call_site_ids_;
   IndexedCollectionVector<MethodHandleItem> method_handle_items_;
   IndexedCollectionVector<StringData> string_datas_;
@@ -442,7 +471,6 @@
   IndexedCollectionVector<AnnotationSetItem> annotation_set_items_;
   IndexedCollectionVector<AnnotationSetRefList> annotation_set_ref_lists_;
   IndexedCollectionVector<AnnotationsDirectoryItem> annotations_directory_items_;
-  IndexedCollectionVector<ClassDef> class_defs_;
   // The order of the vectors controls the layout of the output file by index order, to change the
   // layout just sort the vector. Note that you may only change the order of the non indexed vectors
   // below. Indexed vectors are accessed by indices in other places, changing the sorting order will
@@ -485,6 +513,8 @@
   Item() { }
   virtual ~Item() { }
 
+  Item(Item&&) = default;
+
   // Return the assigned offset.
   uint32_t GetOffset() const WARN_UNUSED {
     CHECK(OffsetAssigned());
@@ -536,18 +566,54 @@
          uint32_t data_size,
          uint32_t data_offset,
          bool support_default_methods)
+      : Item(0, kHeaderItemSize), support_default_methods_(support_default_methods) {
+    ConstructorHelper(magic,
+                      checksum,
+                      signature,
+                      endian_tag,
+                      file_size,
+                      header_size,
+                      link_size,
+                      link_offset,
+                      data_size,
+                      data_offset);
+  }
+
+  Header(const uint8_t* magic,
+         uint32_t checksum,
+         const uint8_t* signature,
+         uint32_t endian_tag,
+         uint32_t file_size,
+         uint32_t header_size,
+         uint32_t link_size,
+         uint32_t link_offset,
+         uint32_t data_size,
+         uint32_t data_offset,
+         bool support_default_methods,
+         uint32_t num_string_ids,
+         uint32_t num_type_ids,
+         uint32_t num_proto_ids,
+         uint32_t num_field_ids,
+         uint32_t num_method_ids,
+         uint32_t num_class_defs)
       : Item(0, kHeaderItemSize),
-        checksum_(checksum),
-        endian_tag_(endian_tag),
-        file_size_(file_size),
-        header_size_(header_size),
-        link_size_(link_size),
-        link_offset_(link_offset),
-        data_size_(data_size),
-        data_offset_(data_offset),
-        support_default_methods_(support_default_methods) {
-    memcpy(magic_, magic, sizeof(magic_));
-    memcpy(signature_, signature, sizeof(signature_));
+        support_default_methods_(support_default_methods),
+        collections_(num_string_ids,
+                     num_type_ids,
+                     num_proto_ids,
+                     num_field_ids,
+                     num_method_ids,
+                     num_class_defs) {
+    ConstructorHelper(magic,
+                      checksum,
+                      signature,
+                      endian_tag,
+                      file_size,
+                      header_size,
+                      link_size,
+                      link_offset,
+                      data_size,
+                      data_offset);
   }
   ~Header() OVERRIDE { }
 
@@ -596,6 +662,27 @@
   uint32_t data_offset_;
   const bool support_default_methods_;
 
+  void ConstructorHelper(const uint8_t* magic,
+                         uint32_t checksum,
+                         const uint8_t* signature,
+                         uint32_t endian_tag,
+                         uint32_t file_size,
+                         uint32_t header_size,
+                         uint32_t link_size,
+                         uint32_t link_offset,
+                         uint32_t data_size,
+                         uint32_t data_offset) {
+    checksum_ = checksum;
+    endian_tag_ = endian_tag;
+    file_size_ = file_size;
+    header_size_ = header_size;
+    link_size_ = link_size;
+    link_offset_ = link_offset;
+    data_size_ = data_size;
+    data_offset_ = data_offset;
+    memcpy(magic_, magic, sizeof(magic_));
+    memcpy(signature_, signature, sizeof(signature_));
+  }
   Collections collections_;
 
   DISALLOW_COPY_AND_ASSIGN(Header);
@@ -744,6 +831,8 @@
       : access_flags_(access_flags), field_id_(field_id) { }
   ~FieldItem() OVERRIDE { }
 
+  FieldItem(FieldItem&&) = default;
+
   uint32_t GetAccessFlags() const { return access_flags_; }
   const FieldId* GetFieldId() const { return field_id_; }
 
@@ -756,7 +845,7 @@
   DISALLOW_COPY_AND_ASSIGN(FieldItem);
 };
 
-using FieldItemVector = std::vector<std::unique_ptr<FieldItem>>;
+using FieldItemVector = std::vector<FieldItem>;
 
 class MethodItem : public Item {
  public:
@@ -764,6 +853,8 @@
       : access_flags_(access_flags), method_id_(method_id), code_(code) { }
   ~MethodItem() OVERRIDE { }
 
+  MethodItem(MethodItem&&) = default;
+
   uint32_t GetAccessFlags() const { return access_flags_; }
   const MethodId* GetMethodId() const { return method_id_; }
   CodeItem* GetCodeItem() { return code_; }
@@ -778,7 +869,7 @@
   DISALLOW_COPY_AND_ASSIGN(MethodItem);
 };
 
-using MethodItemVector = std::vector<std::unique_ptr<MethodItem>>;
+using MethodItemVector = std::vector<MethodItem>;
 
 class EncodedValue {
  public:
diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc
index 4f9bcdd..9468f76 100644
--- a/dexlayout/dex_ir_builder.cc
+++ b/dexlayout/dex_ir_builder.cc
@@ -43,7 +43,13 @@
                               disk_header.link_off_,
                               disk_header.data_size_,
                               disk_header.data_off_,
-                              dex_file.SupportsDefaultMethods());
+                              dex_file.SupportsDefaultMethods(),
+                              dex_file.NumStringIds(),
+                              dex_file.NumTypeIds(),
+                              dex_file.NumProtoIds(),
+                              dex_file.NumFieldIds(),
+                              dex_file.NumMethodIds(),
+                              dex_file.NumClassDefs());
   Collections& collections = header->GetCollections();
   collections.SetEagerlyAssignOffsets(eagerly_assign_offsets);
   // Walk the rest of the header fields.
@@ -94,6 +100,7 @@
 
   // Sort the vectors by the map order (same order as the file).
   collections.SortVectorsByMapOrder();
+  collections.ClearMaps();
 
   // Load the link data if it exists.
   collections.SetLinkData(std::vector<uint8_t>(
diff --git a/dexlayout/dex_verify.cc b/dexlayout/dex_verify.cc
index 18ddc86..2e4756b 100644
--- a/dexlayout/dex_verify.cc
+++ b/dexlayout/dex_verify.cc
@@ -769,8 +769,8 @@
     return false;
   }
   for (size_t i = 0; i < orig->size(); ++i) {
-    dex_ir::FieldItem* orig_field = (*orig)[i].get();
-    dex_ir::FieldItem* output_field = (*output)[i].get();
+    dex_ir::FieldItem* orig_field = &(*orig)[i];
+    dex_ir::FieldItem* output_field = &(*output)[i];
     if (orig_field->GetFieldId()->GetIndex() != output_field->GetFieldId()->GetIndex()) {
       *error_msg = StringPrintf("Mismatched field index for class data at offset %x: %u vs %u.",
                                 orig_offset,
@@ -802,8 +802,8 @@
     return false;
   }
   for (size_t i = 0; i < orig->size(); ++i) {
-    dex_ir::MethodItem* orig_method = (*orig)[i].get();
-    dex_ir::MethodItem* output_method = (*output)[i].get();
+    dex_ir::MethodItem* orig_method = &(*orig)[i];
+    dex_ir::MethodItem* output_method = &(*output)[i];
     if (orig_method->GetMethodId()->GetIndex() != output_method->GetMethodId()->GetIndex()) {
       *error_msg = StringPrintf("Mismatched method index for class data at offset %x: %u vs %u.",
                                 orig_offset,
diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc
index c8aac94..0e04c58 100644
--- a/dexlayout/dex_visualize.cc
+++ b/dexlayout/dex_visualize.cc
@@ -279,22 +279,22 @@
       dumper->DumpAddressRange(class_data, class_index);
       if (class_data->StaticFields()) {
         for (auto& field_item : *class_data->StaticFields()) {
-          dumper->DumpFieldItem(field_item.get(), class_index);
+          dumper->DumpFieldItem(&field_item, class_index);
         }
       }
       if (class_data->InstanceFields()) {
         for (auto& field_item : *class_data->InstanceFields()) {
-          dumper->DumpFieldItem(field_item.get(), class_index);
+          dumper->DumpFieldItem(&field_item, class_index);
         }
       }
       if (class_data->DirectMethods()) {
         for (auto& method_item : *class_data->DirectMethods()) {
-          dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
+          dumper->DumpMethodItem(&method_item, dex_file, class_index, profile_info);
         }
       }
       if (class_data->VirtualMethods()) {
         for (auto& method_item : *class_data->VirtualMethods()) {
-          dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
+          dumper->DumpMethodItem(&method_item, dex_file, class_index, profile_info);
         }
       }
     }
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index eead13f..9ed1312 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -207,21 +207,21 @@
 
 void DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields) {
   uint32_t prev_index = 0;
-  for (std::unique_ptr<dex_ir::FieldItem>& field : *fields) {
-    uint32_t index = field->GetFieldId()->GetIndex();
+  for (auto& field : *fields) {
+    uint32_t index = field.GetFieldId()->GetIndex();
     stream->WriteUleb128(index - prev_index);
-    stream->WriteUleb128(field->GetAccessFlags());
+    stream->WriteUleb128(field.GetAccessFlags());
     prev_index = index;
   }
 }
 
 void DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods) {
   uint32_t prev_index = 0;
-  for (std::unique_ptr<dex_ir::MethodItem>& method : *methods) {
-    uint32_t index = method->GetMethodId()->GetIndex();
-    uint32_t code_off = method->GetCodeItem() == nullptr ? 0 : method->GetCodeItem()->GetOffset();
+  for (auto& method : *methods) {
+    uint32_t index = method.GetMethodId()->GetIndex();
+    uint32_t code_off = method.GetCodeItem() == nullptr ? 0 : method.GetCodeItem()->GetOffset();
     stream->WriteUleb128(index - prev_index);
-    stream->WriteUleb128(method->GetAccessFlags());
+    stream->WriteUleb128(method.GetAccessFlags());
     stream->WriteUleb128(code_off);
     prev_index = index;
   }
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 62dd1a9..39d93bf 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1459,8 +1459,8 @@
     dex_ir::FieldItemVector* static_fields = class_data->StaticFields();
     if (static_fields != nullptr) {
       for (uint32_t i = 0; i < static_fields->size(); i++) {
-        DumpSField((*static_fields)[i]->GetFieldId()->GetIndex(),
-                   (*static_fields)[i]->GetAccessFlags(),
+        DumpSField((*static_fields)[i].GetFieldId()->GetIndex(),
+                   (*static_fields)[i].GetAccessFlags(),
                    i,
                    i < encoded_values_size ? (*encoded_values)[i].get() : nullptr);
       }  // for
@@ -1475,8 +1475,8 @@
     dex_ir::FieldItemVector* instance_fields = class_data->InstanceFields();
     if (instance_fields != nullptr) {
       for (uint32_t i = 0; i < instance_fields->size(); i++) {
-        DumpIField((*instance_fields)[i]->GetFieldId()->GetIndex(),
-                   (*instance_fields)[i]->GetAccessFlags(),
+        DumpIField((*instance_fields)[i].GetFieldId()->GetIndex(),
+                   (*instance_fields)[i].GetAccessFlags(),
                    i);
       }  // for
     }
@@ -1490,9 +1490,9 @@
     dex_ir::MethodItemVector* direct_methods = class_data->DirectMethods();
     if (direct_methods != nullptr) {
       for (uint32_t i = 0; i < direct_methods->size(); i++) {
-        DumpMethod((*direct_methods)[i]->GetMethodId()->GetIndex(),
-                   (*direct_methods)[i]->GetAccessFlags(),
-                   (*direct_methods)[i]->GetCodeItem(),
+        DumpMethod((*direct_methods)[i].GetMethodId()->GetIndex(),
+                   (*direct_methods)[i].GetAccessFlags(),
+                   (*direct_methods)[i].GetCodeItem(),
                  i);
       }  // for
     }
@@ -1506,9 +1506,9 @@
     dex_ir::MethodItemVector* virtual_methods = class_data->VirtualMethods();
     if (virtual_methods != nullptr) {
       for (uint32_t i = 0; i < virtual_methods->size(); i++) {
-        DumpMethod((*virtual_methods)[i]->GetMethodId()->GetIndex(),
-                   (*virtual_methods)[i]->GetAccessFlags(),
-                   (*virtual_methods)[i]->GetCodeItem(),
+        DumpMethod((*virtual_methods)[i].GetMethodId()->GetIndex(),
+                   (*virtual_methods)[i].GetAccessFlags(),
+                   (*virtual_methods)[i].GetCodeItem(),
                    i);
       }  // for
     }
@@ -1636,14 +1636,14 @@
     }
     for (size_t i = 0; i < 2; ++i) {
       for (auto& method : *(i == 0 ? data->DirectMethods() : data->VirtualMethods())) {
-        const dex_ir::MethodId* method_id = method->GetMethodId();
-        dex_ir::CodeItem* code_item = method->GetCodeItem();
+        const dex_ir::MethodId* method_id = method.GetMethodId();
+        dex_ir::CodeItem* code_item = method.GetCodeItem();
         if (code_item == nullptr) {
           continue;
         }
         const bool is_clinit = is_profile_class &&
-            (method->GetAccessFlags() & kAccConstructor) != 0 &&
-            (method->GetAccessFlags() & kAccStatic) != 0;
+            (method.GetAccessFlags() & kAccConstructor) != 0 &&
+            (method.GetAccessFlags() & kAccStatic) != 0;
         const bool method_executed = is_clinit ||
             info_->GetMethodHotness(MethodReference(dex_file, method_id->GetIndex())).IsInProfile();
         if (!method_executed) {
@@ -1744,14 +1744,14 @@
       for (auto& method : *(invoke_type == InvokeType::kDirect
                                 ? class_data->DirectMethods()
                                 : class_data->VirtualMethods())) {
-        const dex_ir::MethodId *method_id = method->GetMethodId();
-        dex_ir::CodeItem *code_item = method->GetCodeItem();
+        const dex_ir::MethodId *method_id = method.GetMethodId();
+        dex_ir::CodeItem *code_item = method.GetCodeItem();
         if (code_item == nullptr) {
           continue;
         }
         // Separate executed methods (clinits and profiled methods) from unexecuted methods.
-        const bool is_clinit = (method->GetAccessFlags() & kAccConstructor) != 0 &&
-            (method->GetAccessFlags() & kAccStatic) != 0;
+        const bool is_clinit = (method.GetAccessFlags() & kAccConstructor) != 0 &&
+            (method.GetAccessFlags() & kAccStatic) != 0;
         const bool is_startup_clinit = is_profile_class && is_clinit;
         using Hotness = ProfileCompilationInfo::MethodHotness;
         Hotness hotness = info_->GetMethodHotness(MethodReference(dex_file, method_id->GetIndex()));
diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h
index 6a714e6..2cc1a31 100644
--- a/libartbase/base/bit_table.h
+++ b/libartbase/base/bit_table.h
@@ -18,6 +18,7 @@
 #define ART_LIBARTBASE_BASE_BIT_TABLE_H_
 
 #include <array>
+#include <initializer_list>
 #include <numeric>
 #include <string.h>
 #include <type_traits>
@@ -184,33 +185,54 @@
 }
 
 // Helper class for encoding BitTable. It can optionally de-duplicate the inputs.
-// Type 'T' must be POD type consisting of uint32_t fields (one for each column).
-template<typename T>
+template<uint32_t kNumColumns>
 class BitTableBuilder {
  public:
-  static_assert(std::is_pod<T>::value, "Type 'T' must be POD");
-  static constexpr size_t kNumColumns = sizeof(T) / sizeof(uint32_t);
+  class Entry {
+   public:
+    Entry() {
+      std::fill_n(data_, kNumColumns, BitTable<kNumColumns>::Accessor::kNoValue);
+    }
+
+    Entry(std::initializer_list<uint32_t> values) {
+      DCHECK_EQ(values.size(), kNumColumns);
+      std::copy(values.begin(), values.end(), data_);
+    }
+
+    uint32_t& operator[](size_t column) {
+      DCHECK_LT(column, kNumColumns);
+      return data_[column];
+    }
+
+    uint32_t operator[](size_t column) const {
+      DCHECK_LT(column, kNumColumns);
+      return data_[column];
+    }
+
+   private:
+    uint32_t data_[kNumColumns];
+  };
 
   explicit BitTableBuilder(ScopedArenaAllocator* allocator)
       : rows_(allocator->Adapter(kArenaAllocBitTableBuilder)),
         dedup_(8, allocator->Adapter(kArenaAllocBitTableBuilder)) {
   }
 
-  T& operator[](size_t row) { return rows_[row]; }
-  const T& operator[](size_t row) const { return rows_[row]; }
+  Entry& operator[](size_t row) { return rows_[row]; }
+  const Entry& operator[](size_t row) const { return rows_[row]; }
   size_t size() const { return rows_.size(); }
 
   // Append given value to the vector without de-duplication.
   // This will not add the element to the dedup map to avoid its associated costs.
-  void Add(T value) {
+  void Add(Entry value) {
     rows_.push_back(value);
   }
 
   // Append given list of values and return the index of the first value.
   // If the exact same set of values was already added, return the old index.
-  uint32_t Dedup(T* values, size_t count = 1) {
+  uint32_t Dedup(Entry* values, size_t count = 1) {
     FNVHash<MemoryRegion> hasher;
-    uint32_t hash = hasher(MemoryRegion(values, sizeof(T) * count));
+    uint32_t hash = hasher(MemoryRegion(values, sizeof(Entry) * count));
 
     // Check if we have already added identical set of values.
     auto range = dedup_.equal_range(hash);
@@ -220,8 +242,8 @@
           std::equal(values,
                      values + count,
                      rows_.begin() + index,
-                     [](const T& lhs, const T& rhs) {
-                       return memcmp(&lhs, &rhs, sizeof(T)) == 0;
+                     [](const Entry& lhs, const Entry& rhs) {
+                       return memcmp(&lhs, &rhs, sizeof(Entry)) == 0;
                      })) {
         return index;
       }
@@ -234,11 +256,8 @@
     return index;
   }
 
-  ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column) const {
-    DCHECK_LT(row, size());
-    DCHECK_LT(column, kNumColumns);
-    const uint32_t* data = reinterpret_cast<const uint32_t*>(&rows_[row]);
-    return data[column];
+  uint32_t Dedup(Entry value) {
+    return Dedup(&value, /* count */ 1);
   }
 
   // Calculate the column bit widths based on the current data.
@@ -247,7 +266,7 @@
     std::fill_n(max_column_value, kNumColumns, 0);
     for (uint32_t r = 0; r < size(); r++) {
       for (uint32_t c = 0; c < kNumColumns; c++) {
-        max_column_value[c] |= Get(r, c) - BitTable<kNumColumns>::kValueBias;
+        max_column_value[c] |= rows_[r][c] - BitTable<kNumColumns>::kValueBias;
       }
     }
     for (uint32_t c = 0; c < kNumColumns; c++) {
@@ -276,7 +295,7 @@
       BitMemoryRegion region(MemoryRegion(out->data(), out->size()));
       for (uint32_t r = 0; r < size(); r++) {
         for (uint32_t c = 0; c < kNumColumns; c++) {
-          region.StoreBitsAndAdvance(bit_offset, Get(r, c) - bias, column_bits[c]);
+          region.StoreBitsAndAdvance(bit_offset, rows_[r][c] - bias, column_bits[c]);
         }
       }
     }
@@ -292,14 +311,14 @@
       }
       for (uint32_t r = 0; r < size(); r++) {
         for (uint32_t c = 0; c < kNumColumns; c++) {
-          DCHECK_EQ(Get(r, c), table.Get(r, c)) << " (" << r << ", " << c << ")";
+          DCHECK_EQ(rows_[r][c], table.Get(r, c)) << " (" << r << ", " << c << ")";
         }
       }
     }
   }
 
  protected:
-  ScopedArenaDeque<T> rows_;
+  ScopedArenaDeque<Entry> rows_;
   ScopedArenaUnorderedMultimap<uint32_t, uint32_t> dedup_;  // Hash -> row index.
 };
 
diff --git a/libartbase/base/bit_table_test.cc b/libartbase/base/bit_table_test.cc
index 8abf0da..969940f 100644
--- a/libartbase/base/bit_table_test.cc
+++ b/libartbase/base/bit_table_test.cc
@@ -50,7 +50,7 @@
 
   std::vector<uint8_t> buffer;
   size_t encode_bit_offset = 0;
-  BitTableBuilder<uint32_t> builder(&allocator);
+  BitTableBuilder<1> builder(&allocator);
   builder.Encode(&buffer, &encode_bit_offset);
 
   size_t decode_bit_offset = 0;
@@ -67,11 +67,11 @@
   constexpr uint32_t kNoValue = -1;
   std::vector<uint8_t> buffer;
   size_t encode_bit_offset = 0;
-  BitTableBuilder<uint32_t> builder(&allocator);
-  builder.Add(42u);
-  builder.Add(kNoValue);
-  builder.Add(1000u);
-  builder.Add(kNoValue);
+  BitTableBuilder<1> builder(&allocator);
+  builder.Add({42u});
+  builder.Add({kNoValue});
+  builder.Add({1000u});
+  builder.Add({kNoValue});
   builder.Encode(&buffer, &encode_bit_offset);
 
   size_t decode_bit_offset = 0;
@@ -93,8 +93,8 @@
   for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) {
     std::vector<uint8_t> buffer;
     size_t encode_bit_offset = start_bit_offset;
-    BitTableBuilder<uint32_t> builder(&allocator);
-    builder.Add(42u);
+    BitTableBuilder<1> builder(&allocator);
+    builder.Add({42u});
     builder.Encode(&buffer, &encode_bit_offset);
 
     size_t decode_bit_offset = start_bit_offset;
@@ -113,15 +113,9 @@
   constexpr uint32_t kNoValue = -1;
   std::vector<uint8_t> buffer;
   size_t encode_bit_offset = 0;
-  struct RowData {
-    uint32_t a;
-    uint32_t b;
-    uint32_t c;
-    uint32_t d;
-  };
-  BitTableBuilder<RowData> builder(&allocator);
-  builder.Add(RowData{42u, kNoValue, 0u, static_cast<uint32_t>(-2)});
-  builder.Add(RowData{62u, kNoValue, 63u, static_cast<uint32_t>(-3)});
+  BitTableBuilder<4> builder(&allocator);
+  builder.Add({42u, kNoValue, 0u, static_cast<uint32_t>(-2)});
+  builder.Add({62u, kNoValue, 63u, static_cast<uint32_t>(-3)});
   builder.Encode(&buffer, &encode_bit_offset);
 
   size_t decode_bit_offset = 0;
@@ -147,13 +141,9 @@
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
 
-  struct RowData {
-    uint32_t a;
-    uint32_t b;
-  };
-  BitTableBuilder<RowData> builder(&allocator);
-  RowData value0{1, 2};
-  RowData value1{3, 4};
+  BitTableBuilder<2> builder(&allocator);
+  BitTableBuilder<2>::Entry value0{1, 2};
+  BitTableBuilder<2>::Entry value1{3, 4};
   EXPECT_EQ(0u, builder.Dedup(&value0));
   EXPECT_EQ(1u, builder.Dedup(&value1));
   EXPECT_EQ(0u, builder.Dedup(&value0));
@@ -197,16 +187,12 @@
   ScopedArenaAllocator allocator(&arena_stack);
   FNVHash<MemoryRegion> hasher;
 
-  struct RowData {
-    uint32_t a;
-    uint32_t b;
-  };
-  RowData value0{56948505, 0};
-  RowData value1{67108869, 0};
+  BitTableBuilder<2>::Entry value0{56948505, 0};
+  BitTableBuilder<2>::Entry value1{67108869, 0};
 
-  BitTableBuilder<RowData> builder(&allocator);
-  EXPECT_EQ(hasher(MemoryRegion(&value0, sizeof(RowData))),
-            hasher(MemoryRegion(&value1, sizeof(RowData))));
+  BitTableBuilder<2> builder(&allocator);
+  EXPECT_EQ(hasher(MemoryRegion(&value0, sizeof(value0))),
+            hasher(MemoryRegion(&value1, sizeof(value1))));
   EXPECT_EQ(0u, builder.Dedup(&value0));
   EXPECT_EQ(1u, builder.Dedup(&value1));
   EXPECT_EQ(0u, builder.Dedup(&value0));
@@ -214,12 +200,12 @@
   EXPECT_EQ(2u, builder.size());
 
   BitmapTableBuilder builder2(&allocator);
-  EXPECT_EQ(hasher(MemoryRegion(&value0, BitsToBytesRoundUp(MinimumBitsToStore(value0.a)))),
-            hasher(MemoryRegion(&value1, BitsToBytesRoundUp(MinimumBitsToStore(value1.a)))));
-  EXPECT_EQ(0u, builder2.Dedup(&value0.a, MinimumBitsToStore(value0.a)));
-  EXPECT_EQ(1u, builder2.Dedup(&value1.a, MinimumBitsToStore(value1.a)));
-  EXPECT_EQ(0u, builder2.Dedup(&value0.a, MinimumBitsToStore(value0.a)));
-  EXPECT_EQ(1u, builder2.Dedup(&value1.a, MinimumBitsToStore(value1.a)));
+  EXPECT_EQ(hasher(MemoryRegion(&value0, BitsToBytesRoundUp(MinimumBitsToStore(value0[0])))),
+            hasher(MemoryRegion(&value1, BitsToBytesRoundUp(MinimumBitsToStore(value1[0])))));
+  EXPECT_EQ(0u, builder2.Dedup(&value0[0], MinimumBitsToStore(value0[0])));
+  EXPECT_EQ(1u, builder2.Dedup(&value1[0], MinimumBitsToStore(value1[0])));
+  EXPECT_EQ(0u, builder2.Dedup(&value0[0], MinimumBitsToStore(value0[0])));
+  EXPECT_EQ(1u, builder2.Dedup(&value1[0], MinimumBitsToStore(value1[0])));
   EXPECT_EQ(2u, builder2.size());
 }
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 2db0283..2b0095c 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -546,28 +546,30 @@
         os << "\n";
       }
 
-      // Dump .bss entries.
-      DumpBssEntries(
-          os,
-          "ArtMethod",
-          oat_dex_file->GetMethodBssMapping(),
-          dex_file->NumMethodIds(),
-          static_cast<size_t>(GetInstructionSetPointerSize(instruction_set_)),
-          [=](uint32_t index) { return dex_file->PrettyMethod(index); });
-      DumpBssEntries(
-          os,
-          "Class",
-          oat_dex_file->GetTypeBssMapping(),
-          dex_file->NumTypeIds(),
-          sizeof(GcRoot<mirror::Class>),
-          [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
-      DumpBssEntries(
-          os,
-          "String",
-          oat_dex_file->GetStringBssMapping(),
-          dex_file->NumStringIds(),
-          sizeof(GcRoot<mirror::Class>),
-          [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); });
+      if (!options_.dump_header_only_) {
+        // Dump .bss entries.
+        DumpBssEntries(
+            os,
+            "ArtMethod",
+            oat_dex_file->GetMethodBssMapping(),
+            dex_file->NumMethodIds(),
+            static_cast<size_t>(GetInstructionSetPointerSize(instruction_set_)),
+            [=](uint32_t index) { return dex_file->PrettyMethod(index); });
+        DumpBssEntries(
+            os,
+            "Class",
+            oat_dex_file->GetTypeBssMapping(),
+            dex_file->NumTypeIds(),
+            sizeof(GcRoot<mirror::Class>),
+            [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
+        DumpBssEntries(
+            os,
+            "String",
+            oat_dex_file->GetStringBssMapping(),
+            dex_file->NumStringIds(),
+            sizeof(GcRoot<mirror::Class>),
+            [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); });
+      }
     }
 
     if (!options_.dump_header_only_) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 1710e78..c374e03 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4440,8 +4440,8 @@
 
   // Find the <init>(InvocationHandler)V method. The exact method offset varies depending
   // on which front-end compiler was used to build the libcore DEX files.
-  ArtMethod* proxy_constructor = proxy_class->FindConstructor(
-      "(Ljava/lang/reflect/InvocationHandler;)V", image_pointer_size_);
+  ArtMethod* proxy_constructor =
+      jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Proxy_init);
   DCHECK(proxy_constructor != nullptr)
       << "Could not find <init> method in java.lang.reflect.Proxy";
 
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2b1623a..3ccfa55 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1101,11 +1101,14 @@
   // that part.
   ScopedQuickEntrypointChecks sqec(self, kIsDebugBuild, false);
   instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+  DCHECK(!method->IsProxyMethod())
+      << "Proxy method " << method->PrettyMethod()
+      << " (declaring class: " << method->GetDeclaringClass()->PrettyClass() << ")"
+      << " should not hit instrumentation entrypoint.";
   if (instrumentation->IsDeoptimized(method)) {
     result = GetQuickToInterpreterBridge();
   } else {
     result = instrumentation->GetQuickCodeFor(method, kRuntimePointerSize);
-    DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result));
   }
 
   bool interpreter_entry = (result == GetQuickToInterpreterBridge());
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index b331e97..36fefbd 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -32,7 +32,9 @@
 namespace collector {
 
 inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegion(
-    mirror::Object* ref, accounting::ContinuousSpaceBitmap* bitmap) {
+    Thread* const self,
+    mirror::Object* ref,
+    accounting::ContinuousSpaceBitmap* bitmap) {
   // For the Baker-style RB, in a rare case, we could incorrectly change the object from white
   // to gray even though the object has already been marked through. This happens if a mutator
   // thread gets preempted before the AtomicSetReadBarrierState below, GC marks through the
@@ -63,20 +65,21 @@
     if (kUseBakerReadBarrier) {
       DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
     }
-    PushOntoMarkStack(ref);
+    PushOntoMarkStack(self, ref);
   }
   return ref;
 }
 
 template<bool kGrayImmuneObject>
-inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) {
+inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(Thread* const self,
+                                                          mirror::Object* ref) {
   if (kUseBakerReadBarrier) {
     // The GC-running thread doesn't (need to) gray immune objects except when updating thread roots
     // in the thread flip on behalf of suspended threads (when gc_grays_immune_objects_ is
     // true). Also, a mutator doesn't (need to) gray an immune object after GC has updated all
     // immune space objects (when updated_all_immune_objects_ is true).
     if (kIsDebugBuild) {
-      if (Thread::Current() == thread_running_gc_) {
+      if (self == thread_running_gc_) {
         DCHECK(!kGrayImmuneObject ||
                updated_all_immune_objects_.load(std::memory_order_relaxed) ||
                gc_grays_immune_objects_);
@@ -91,7 +94,7 @@
     bool success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::WhiteState(),
                                                   /* rb_state */ ReadBarrier::GrayState());
     if (success) {
-      MutexLock mu(Thread::Current(), immune_gray_stack_lock_);
+      MutexLock mu(self, immune_gray_stack_lock_);
       immune_gray_stack_.push_back(ref);
     }
   }
@@ -99,7 +102,8 @@
 }
 
 template<bool kGrayImmuneObject, bool kFromGCThread>
-inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref,
+inline mirror::Object* ConcurrentCopying::Mark(Thread* const self,
+                                               mirror::Object* from_ref,
                                                mirror::Object* holder,
                                                MemberOffset offset) {
   if (from_ref == nullptr) {
@@ -108,7 +112,7 @@
   DCHECK(heap_->collector_type_ == kCollectorTypeCC);
   if (kFromGCThread) {
     DCHECK(is_active_);
-    DCHECK_EQ(Thread::Current(), thread_running_gc_);
+    DCHECK_EQ(self, thread_running_gc_);
   } else if (UNLIKELY(kUseBakerReadBarrier && !is_active_)) {
     // In the lock word forward address state, the read barrier bits
     // in the lock word are part of the stored forwarding address and
@@ -134,7 +138,7 @@
         mirror::Object* to_ref = GetFwdPtr(from_ref);
         if (to_ref == nullptr) {
           // It isn't marked yet. Mark it by copying it to the to-space.
-          to_ref = Copy(from_ref, holder, offset);
+          to_ref = Copy(self, from_ref, holder, offset);
         }
         // The copy should either be in a to-space region, or in the
         // non-moving space, if it could not fit in a to-space region.
@@ -143,7 +147,7 @@
         return to_ref;
       }
       case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace:
-        return MarkUnevacFromSpaceRegion(from_ref, region_space_bitmap_);
+        return MarkUnevacFromSpaceRegion(self, from_ref, region_space_bitmap_);
       default:
         // The reference is in an unused region.
         LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(holder, offset, from_ref);
@@ -153,24 +157,25 @@
     }
   } else {
     if (immune_spaces_.ContainsObject(from_ref)) {
-      return MarkImmuneSpace<kGrayImmuneObject>(from_ref);
+      return MarkImmuneSpace<kGrayImmuneObject>(self, from_ref);
     } else {
-      return MarkNonMoving(from_ref, holder, offset);
+      return MarkNonMoving(self, from_ref, holder, offset);
     }
   }
 }
 
 inline mirror::Object* ConcurrentCopying::MarkFromReadBarrier(mirror::Object* from_ref) {
   mirror::Object* ret;
+  Thread* const self = Thread::Current();
   // We can get here before marking starts since we gray immune objects before the marking phase.
-  if (from_ref == nullptr || !Thread::Current()->GetIsGcMarking()) {
+  if (from_ref == nullptr || !self->GetIsGcMarking()) {
     return from_ref;
   }
   // TODO: Consider removing this check when we are done investigating slow paths. b/30162165
   if (UNLIKELY(mark_from_read_barrier_measurements_)) {
-    ret = MarkFromReadBarrierWithMeasurements(from_ref);
+    ret = MarkFromReadBarrierWithMeasurements(self, from_ref);
   } else {
-    ret = Mark(from_ref);
+    ret = Mark(self, from_ref);
   }
   // Only set the mark bit for baker barrier.
   if (kUseBakerReadBarrier && LIKELY(!rb_mark_bit_stack_full_ && ret->AtomicSetMarkBit(0, 1))) {
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 2c2c437..c4d2fdd 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -129,13 +129,14 @@
 
 void ConcurrentCopying::MarkHeapReference(mirror::HeapReference<mirror::Object>* field,
                                           bool do_atomic_update) {
+  Thread* const self = Thread::Current();
   if (UNLIKELY(do_atomic_update)) {
     // Used to mark the referent in DelayReferenceReferent in transaction mode.
     mirror::Object* from_ref = field->AsMirrorPtr();
     if (from_ref == nullptr) {
       return;
     }
-    mirror::Object* to_ref = Mark(from_ref);
+    mirror::Object* to_ref = Mark(self, from_ref);
     if (from_ref != to_ref) {
       do {
         if (field->AsMirrorPtr() != from_ref) {
@@ -148,7 +149,7 @@
     // Used for preserving soft references, should be OK to not have a CAS here since there should be
     // no other threads which can trigger read barriers on the same referent during reference
     // processing.
-    field->Assign(Mark(field->AsMirrorPtr()));
+    field->Assign(Mark(self, field->AsMirrorPtr()));
   }
 }
 
@@ -300,6 +301,8 @@
   immune_spaces_.Reset();
   bytes_moved_.store(0, std::memory_order_relaxed);
   objects_moved_.store(0, std::memory_order_relaxed);
+  bytes_moved_gc_thread_ = 0;
+  objects_moved_gc_thread_ = 0;
   GcCause gc_cause = GetCurrentIteration()->GetGcCause();
   if (gc_cause == kGcCauseExplicit ||
       gc_cause == kGcCauseCollectorTransition ||
@@ -370,11 +373,12 @@
                   size_t count,
                   const RootInfo& info ATTRIBUTE_UNUSED)
       REQUIRES_SHARED(Locks::mutator_lock_) {
+    Thread* self = Thread::Current();
     for (size_t i = 0; i < count; ++i) {
       mirror::Object** root = roots[i];
       mirror::Object* ref = *root;
       if (ref != nullptr) {
-        mirror::Object* to_ref = concurrent_copying_->Mark(ref);
+        mirror::Object* to_ref = concurrent_copying_->Mark(self, ref);
         if (to_ref != ref) {
           *root = to_ref;
         }
@@ -386,11 +390,12 @@
                   size_t count,
                   const RootInfo& info ATTRIBUTE_UNUSED)
       REQUIRES_SHARED(Locks::mutator_lock_) {
+    Thread* self = Thread::Current();
     for (size_t i = 0; i < count; ++i) {
       mirror::CompressedReference<mirror::Object>* const root = roots[i];
       if (!root->IsNull()) {
         mirror::Object* ref = root->AsMirrorPtr();
-        mirror::Object* to_ref = concurrent_copying_->Mark(ref);
+        mirror::Object* to_ref = concurrent_copying_->Mark(self, ref);
         if (to_ref != ref) {
           root->Assign(to_ref);
         }
@@ -452,7 +457,7 @@
     // This is safe since single threaded behavior should mean FillDummyObject does not
     // happen when java_lang_Object_ is null.
     if (WellKnownClasses::java_lang_Object != nullptr) {
-      cc->java_lang_Object_ = down_cast<mirror::Class*>(cc->Mark(
+      cc->java_lang_Object_ = down_cast<mirror::Class*>(cc->Mark(thread,
           WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object).Ptr()));
     } else {
       cc->java_lang_Object_ = nullptr;
@@ -1024,10 +1029,10 @@
   mark_stack_mode_.store(kMarkStackModeOff, std::memory_order_seq_cst);
 }
 
-void ConcurrentCopying::PushOntoFalseGrayStack(mirror::Object* ref) {
+void ConcurrentCopying::PushOntoFalseGrayStack(Thread* const self, mirror::Object* ref) {
   CHECK(kUseBakerReadBarrier);
   DCHECK(ref != nullptr);
-  MutexLock mu(Thread::Current(), mark_stack_lock_);
+  MutexLock mu(self, mark_stack_lock_);
   false_gray_stack_.push_back(ref);
 }
 
@@ -1070,10 +1075,9 @@
   DCHECK(!gc_mark_stack_->IsFull());
 }
 
-void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) {
+void ConcurrentCopying::PushOntoMarkStack(Thread* const self, mirror::Object* to_ref) {
   CHECK_EQ(is_mark_stack_push_disallowed_.load(std::memory_order_relaxed), 0)
       << " " << to_ref << " " << mirror::Object::PrettyTypeOf(to_ref);
-  Thread* self = Thread::Current();  // TODO: pass self as an argument from call sites?
   CHECK(thread_running_gc_ != nullptr);
   MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed);
   if (LIKELY(mark_stack_mode == kMarkStackModeThreadLocal)) {
@@ -1409,10 +1413,10 @@
 }
 
 bool ConcurrentCopying::ProcessMarkStackOnce() {
-  Thread* self = Thread::Current();
-  CHECK(thread_running_gc_ != nullptr);
-  CHECK(self == thread_running_gc_);
-  CHECK(self->GetThreadLocalMarkStack() == nullptr);
+  DCHECK(thread_running_gc_ != nullptr);
+  Thread* const self = Thread::Current();
+  DCHECK(self == thread_running_gc_);
+  DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr);
   size_t count = 0;
   MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed);
   if (mark_stack_mode == kMarkStackModeThreadLocal) {
@@ -1432,14 +1436,14 @@
     IssueEmptyCheckpoint();
     // Process the shared GC mark stack with a lock.
     {
-      MutexLock mu(self, mark_stack_lock_);
+      MutexLock mu(thread_running_gc_, mark_stack_lock_);
       CHECK(revoked_mark_stacks_.empty());
     }
     while (true) {
       std::vector<mirror::Object*> refs;
       {
         // Copy refs with lock. Note the number of refs should be small.
-        MutexLock mu(self, mark_stack_lock_);
+        MutexLock mu(thread_running_gc_, mark_stack_lock_);
         if (gc_mark_stack_->IsEmpty()) {
           break;
         }
@@ -1458,7 +1462,7 @@
     CHECK_EQ(static_cast<uint32_t>(mark_stack_mode),
              static_cast<uint32_t>(kMarkStackModeGcExclusive));
     {
-      MutexLock mu(self, mark_stack_lock_);
+      MutexLock mu(thread_running_gc_, mark_stack_lock_);
       CHECK(revoked_mark_stacks_.empty());
     }
     // Process the GC mark stack in the exclusive mode. No need to take the lock.
@@ -1481,7 +1485,7 @@
   size_t count = 0;
   std::vector<accounting::AtomicStack<mirror::Object>*> mark_stacks;
   {
-    MutexLock mu(Thread::Current(), mark_stack_lock_);
+    MutexLock mu(thread_running_gc_, mark_stack_lock_);
     // Make a copy of the mark stack vector.
     mark_stacks = revoked_mark_stacks_;
     revoked_mark_stacks_.clear();
@@ -1493,7 +1497,7 @@
       ++count;
     }
     {
-      MutexLock mu(Thread::Current(), mark_stack_lock_);
+      MutexLock mu(thread_running_gc_, mark_stack_lock_);
       if (pooled_mark_stacks_.size() >= kMarkStackPoolSize) {
         // The pool has enough. Delete it.
         delete mark_stack;
@@ -1596,9 +1600,9 @@
 
 void ConcurrentCopying::SwitchToSharedMarkStackMode() {
   Thread* self = Thread::Current();
-  CHECK(thread_running_gc_ != nullptr);
-  CHECK_EQ(self, thread_running_gc_);
-  CHECK(self->GetThreadLocalMarkStack() == nullptr);
+  DCHECK(thread_running_gc_ != nullptr);
+  DCHECK(self == thread_running_gc_);
+  DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr);
   MarkStackMode before_mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed);
   CHECK_EQ(static_cast<uint32_t>(before_mark_stack_mode),
            static_cast<uint32_t>(kMarkStackModeThreadLocal));
@@ -1614,9 +1618,9 @@
 
 void ConcurrentCopying::SwitchToGcExclusiveMarkStackMode() {
   Thread* self = Thread::Current();
-  CHECK(thread_running_gc_ != nullptr);
-  CHECK_EQ(self, thread_running_gc_);
-  CHECK(self->GetThreadLocalMarkStack() == nullptr);
+  DCHECK(thread_running_gc_ != nullptr);
+  DCHECK(self == thread_running_gc_);
+  DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr);
   MarkStackMode before_mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed);
   CHECK_EQ(static_cast<uint32_t>(before_mark_stack_mode),
            static_cast<uint32_t>(kMarkStackModeShared));
@@ -1629,14 +1633,14 @@
 
 void ConcurrentCopying::CheckEmptyMarkStack() {
   Thread* self = Thread::Current();
-  CHECK(thread_running_gc_ != nullptr);
-  CHECK_EQ(self, thread_running_gc_);
-  CHECK(self->GetThreadLocalMarkStack() == nullptr);
+  DCHECK(thread_running_gc_ != nullptr);
+  DCHECK(self == thread_running_gc_);
+  DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr);
   MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed);
   if (mark_stack_mode == kMarkStackModeThreadLocal) {
     // Thread-local mark stack mode.
     RevokeThreadLocalMarkStacks(false, nullptr);
-    MutexLock mu(Thread::Current(), mark_stack_lock_);
+    MutexLock mu(thread_running_gc_, mark_stack_lock_);
     if (!revoked_mark_stacks_.empty()) {
       for (accounting::AtomicStack<mirror::Object>* mark_stack : revoked_mark_stacks_) {
         while (!mark_stack->IsEmpty()) {
@@ -1655,7 +1659,7 @@
     }
   } else {
     // Shared, GC-exclusive, or off.
-    MutexLock mu(Thread::Current(), mark_stack_lock_);
+    MutexLock mu(thread_running_gc_, mark_stack_lock_);
     CHECK(gc_mark_stack_->IsEmpty());
     CHECK(revoked_mark_stacks_.empty());
   }
@@ -1755,9 +1759,9 @@
     const uint64_t from_objects = region_space_->GetObjectsAllocatedInFromSpace();
     const uint64_t unevac_from_bytes = region_space_->GetBytesAllocatedInUnevacFromSpace();
     const uint64_t unevac_from_objects = region_space_->GetObjectsAllocatedInUnevacFromSpace();
-    uint64_t to_bytes = bytes_moved_.load(std::memory_order_seq_cst);
+    uint64_t to_bytes = bytes_moved_.load(std::memory_order_seq_cst) + bytes_moved_gc_thread_;
     cumulative_bytes_moved_.fetch_add(to_bytes, std::memory_order_relaxed);
-    uint64_t to_objects = objects_moved_.load(std::memory_order_seq_cst);
+    uint64_t to_objects = objects_moved_.load(std::memory_order_seq_cst) + objects_moved_gc_thread_;
     cumulative_objects_moved_.fetch_add(to_objects, std::memory_order_relaxed);
     if (kEnableFromSpaceAccountingCheck) {
       CHECK_EQ(from_space_num_objects_at_first_pause_, from_objects + unevac_from_objects);
@@ -2075,8 +2079,8 @@
 // Used to scan ref fields of an object.
 class ConcurrentCopying::RefFieldsVisitor {
  public:
-  explicit RefFieldsVisitor(ConcurrentCopying* collector)
-      : collector_(collector) {}
+  explicit RefFieldsVisitor(ConcurrentCopying* collector, Thread* const thread)
+      : collector_(collector), thread_(thread) {}
 
   void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */)
       const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_)
@@ -2101,11 +2105,12 @@
   void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
       ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    collector_->MarkRoot</*kGrayImmuneObject*/false>(root);
+    collector_->MarkRoot</*kGrayImmuneObject*/false>(thread_, root);
   }
 
  private:
   ConcurrentCopying* const collector_;
+  Thread* const thread_;
 };
 
 inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
@@ -2117,12 +2122,12 @@
   }
   DCHECK(!region_space_->IsInFromSpace(to_ref));
   DCHECK_EQ(Thread::Current(), thread_running_gc_);
-  RefFieldsVisitor visitor(this);
+  RefFieldsVisitor visitor(this, thread_running_gc_);
   // Disable the read barrier for a performance reason.
   to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
       visitor, visitor);
   if (kDisallowReadBarrierDuringScan && !Runtime::Current()->IsActiveTransaction()) {
-    Thread::Current()->ModifyDebugDisallowReadBarrier(-1);
+    thread_running_gc_->ModifyDebugDisallowReadBarrier(-1);
   }
 }
 
@@ -2131,6 +2136,7 @@
   mirror::Object* ref = obj->GetFieldObject<
       mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
   mirror::Object* to_ref = Mark</*kGrayImmuneObject*/false, /*kFromGCThread*/true>(
+      thread_running_gc_,
       ref,
       /*holder*/ obj,
       offset);
@@ -2156,10 +2162,11 @@
 // Process some roots.
 inline void ConcurrentCopying::VisitRoots(
     mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) {
+  Thread* const self = Thread::Current();
   for (size_t i = 0; i < count; ++i) {
     mirror::Object** root = roots[i];
     mirror::Object* ref = *root;
-    mirror::Object* to_ref = Mark(ref);
+    mirror::Object* to_ref = Mark(self, ref);
     if (to_ref == ref) {
       continue;
     }
@@ -2176,10 +2183,11 @@
 }
 
 template<bool kGrayImmuneObject>
-inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference<mirror::Object>* root) {
+inline void ConcurrentCopying::MarkRoot(Thread* const self,
+                                        mirror::CompressedReference<mirror::Object>* root) {
   DCHECK(!root->IsNull());
   mirror::Object* const ref = root->AsMirrorPtr();
-  mirror::Object* to_ref = Mark<kGrayImmuneObject>(ref);
+  mirror::Object* to_ref = Mark<kGrayImmuneObject>(self, ref);
   if (to_ref != ref) {
     auto* addr = reinterpret_cast<Atomic<mirror::CompressedReference<mirror::Object>>*>(root);
     auto expected_ref = mirror::CompressedReference<mirror::Object>::FromMirrorPtr(ref);
@@ -2197,11 +2205,12 @@
 inline void ConcurrentCopying::VisitRoots(
     mirror::CompressedReference<mirror::Object>** roots, size_t count,
     const RootInfo& info ATTRIBUTE_UNUSED) {
+  Thread* const self = Thread::Current();
   for (size_t i = 0; i < count; ++i) {
     mirror::CompressedReference<mirror::Object>* const root = roots[i];
     if (!root->IsNull()) {
       // kGrayImmuneObject is true because this is used for the thread flip.
-      MarkRoot</*kGrayImmuneObject*/true>(root);
+      MarkRoot</*kGrayImmuneObject*/true>(self, root);
     }
   }
 }
@@ -2235,7 +2244,9 @@
 
 // Fill the given memory block with a dummy object. Used to fill in a
 // copy of objects that was lost in race.
-void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size) {
+void ConcurrentCopying::FillWithDummyObject(Thread* const self,
+                                            mirror::Object* dummy_obj,
+                                            size_t byte_size) {
   // GC doesn't gray immune objects while scanning immune objects. But we need to trigger the read
   // barriers here because we need the updated reference to the int array class, etc. Temporary set
   // gc_grays_immune_objects_ to true so that we won't cause a DCHECK failure in MarkImmuneSpace().
@@ -2245,7 +2256,7 @@
   // Avoid going through read barrier for since kDisallowReadBarrierDuringScan may be enabled.
   // Explicitly mark to make sure to get an object in the to-space.
   mirror::Class* int_array_class = down_cast<mirror::Class*>(
-      Mark(GetClassRoot<mirror::IntArray, kWithoutReadBarrier>().Ptr()));
+      Mark(self, GetClassRoot<mirror::IntArray, kWithoutReadBarrier>().Ptr()));
   CHECK(int_array_class != nullptr);
   if (ReadBarrier::kEnableToSpaceInvariantChecks) {
     AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class);
@@ -2279,10 +2290,9 @@
 }
 
 // Reuse the memory blocks that were copy of objects that were lost in race.
-mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) {
+mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(Thread* const self, size_t alloc_size) {
   // Try to reuse the blocks that were unused due to CAS failures.
   CHECK_ALIGNED(alloc_size, space::RegionSpace::kAlignment);
-  Thread* self = Thread::Current();
   size_t min_object_size = RoundUp(sizeof(mirror::Object), space::RegionSpace::kAlignment);
   size_t byte_size;
   uint8_t* addr;
@@ -2326,7 +2336,8 @@
     // FillWithDummyObject may mark an object, avoid holding skipped_blocks_lock_ to prevent lock
     // violation and possible deadlock. The deadlock case is a recursive case:
     // FillWithDummyObject -> Mark(IntArray.class) -> Copy -> AllocateInSkippedBlock.
-    FillWithDummyObject(reinterpret_cast<mirror::Object*>(addr + alloc_size),
+    FillWithDummyObject(self,
+                        reinterpret_cast<mirror::Object*>(addr + alloc_size),
                         byte_size - alloc_size);
     CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr + alloc_size)));
     {
@@ -2337,7 +2348,8 @@
   return reinterpret_cast<mirror::Object*>(addr);
 }
 
-mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,
+mirror::Object* ConcurrentCopying::Copy(Thread* const self,
+                                        mirror::Object* from_ref,
                                         mirror::Object* holder,
                                         MemberOffset offset) {
   DCHECK(region_space_->IsInFromSpace(from_ref));
@@ -2366,7 +2378,7 @@
     DCHECK_EQ(region_space_alloc_size, region_space_bytes_allocated);
   } else {
     // Failed to allocate in the region space. Try the skipped blocks.
-    to_ref = AllocateInSkippedBlock(region_space_alloc_size);
+    to_ref = AllocateInSkippedBlock(self, region_space_alloc_size);
     if (to_ref != nullptr) {
       // Succeeded to allocate in a skipped block.
       if (heap_->use_tlab_) {
@@ -2386,7 +2398,7 @@
                   << " skipped_objects="
                   << to_space_objects_skipped_.load(std::memory_order_seq_cst);
       }
-      to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size,
+      to_ref = heap_->non_moving_space_->Alloc(self, obj_size,
                                                &non_moving_space_bytes_allocated, nullptr, &dummy);
       if (UNLIKELY(to_ref == nullptr)) {
         LOG(FATAL_WITHOUT_ABORT) << "Fall-back non-moving space allocation failed for a "
@@ -2427,7 +2439,7 @@
       // the forwarding pointer first. Make the lost copy (to_ref)
       // look like a valid but dead (dummy) object and keep it for
       // future reuse.
-      FillWithDummyObject(to_ref, bytes_allocated);
+      FillWithDummyObject(self, to_ref, bytes_allocated);
       if (!fall_back_to_non_moving) {
         DCHECK(region_space_->IsInToSpace(to_ref));
         if (bytes_allocated > space::RegionSpace::kRegionSize) {
@@ -2438,7 +2450,7 @@
           heap_->num_bytes_allocated_.fetch_add(bytes_allocated, std::memory_order_seq_cst);
           to_space_bytes_skipped_.fetch_add(bytes_allocated, std::memory_order_seq_cst);
           to_space_objects_skipped_.fetch_add(1, std::memory_order_seq_cst);
-          MutexLock mu(Thread::Current(), skipped_blocks_lock_);
+          MutexLock mu(self, skipped_blocks_lock_);
           skipped_blocks_map_.insert(std::make_pair(bytes_allocated,
                                                     reinterpret_cast<uint8_t*>(to_ref)));
         }
@@ -2450,7 +2462,7 @@
             heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
         CHECK(mark_bitmap != nullptr);
         CHECK(mark_bitmap->Clear(to_ref));
-        heap_->non_moving_space_->Free(Thread::Current(), to_ref);
+        heap_->non_moving_space_->Free(self, to_ref);
       }
 
       // Get the winner's forward ptr.
@@ -2481,8 +2493,15 @@
     bool success = from_ref->CasLockWordWeakRelaxed(old_lock_word, new_lock_word);
     if (LIKELY(success)) {
       // The CAS succeeded.
-      objects_moved_.fetch_add(1, std::memory_order_relaxed);
-      bytes_moved_.fetch_add(region_space_alloc_size, std::memory_order_relaxed);
+      DCHECK(thread_running_gc_ != nullptr);
+      if (LIKELY(self == thread_running_gc_)) {
+        objects_moved_gc_thread_ += 1;
+        bytes_moved_gc_thread_ += region_space_alloc_size;
+      } else {
+        objects_moved_.fetch_add(1, std::memory_order_relaxed);
+        bytes_moved_.fetch_add(region_space_alloc_size, std::memory_order_relaxed);
+      }
+
       if (LIKELY(!fall_back_to_non_moving)) {
         DCHECK(region_space_->IsInToSpace(to_ref));
       } else {
@@ -2494,7 +2513,7 @@
       }
       DCHECK(GetFwdPtr(from_ref) == to_ref);
       CHECK_NE(to_ref->GetLockWord(false).GetState(), LockWord::kForwardingAddress);
-      PushOntoMarkStack(to_ref);
+      PushOntoMarkStack(self, to_ref);
       return to_ref;
     } else {
       // The CAS failed. It may have lost the race or may have failed
@@ -2573,7 +2592,8 @@
   return alloc_stack->Contains(ref);
 }
 
-mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref,
+mirror::Object* ConcurrentCopying::MarkNonMoving(Thread* const self,
+                                                 mirror::Object* ref,
                                                  mirror::Object* holder,
                                                  MemberOffset offset) {
   // ref is in a non-moving space (from_ref == to_ref).
@@ -2636,20 +2656,20 @@
         // Already marked.
         if (kUseBakerReadBarrier && cas_success &&
             ref->GetReadBarrierState() == ReadBarrier::GrayState()) {
-          PushOntoFalseGrayStack(ref);
+          PushOntoFalseGrayStack(self, ref);
         }
       } else if (is_los && los_bitmap->AtomicTestAndSet(ref)) {
         // Already marked in LOS.
         if (kUseBakerReadBarrier && cas_success &&
             ref->GetReadBarrierState() == ReadBarrier::GrayState()) {
-          PushOntoFalseGrayStack(ref);
+          PushOntoFalseGrayStack(self, ref);
         }
       } else {
         // Newly marked.
         if (kUseBakerReadBarrier) {
           DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
         }
-        PushOntoMarkStack(ref);
+        PushOntoMarkStack(self, ref);
       }
     }
   }
@@ -2742,7 +2762,7 @@
 }
 
 mirror::Object* ConcurrentCopying::MarkObject(mirror::Object* from_ref) {
-  return Mark(from_ref);
+  return Mark(Thread::Current(), from_ref);
 }
 
 void ConcurrentCopying::DelayReferenceReferent(ObjPtr<mirror::Class> klass,
@@ -2763,15 +2783,16 @@
   region_space_->RevokeAllThreadLocalBuffers();
 }
 
-mirror::Object* ConcurrentCopying::MarkFromReadBarrierWithMeasurements(mirror::Object* from_ref) {
-  if (Thread::Current() != thread_running_gc_) {
+mirror::Object* ConcurrentCopying::MarkFromReadBarrierWithMeasurements(Thread* const self,
+                                                                       mirror::Object* from_ref) {
+  if (self != thread_running_gc_) {
     rb_slow_path_count_.fetch_add(1u, std::memory_order_relaxed);
   } else {
     rb_slow_path_count_gc_.fetch_add(1u, std::memory_order_relaxed);
   }
   ScopedTrace tr(__FUNCTION__);
   const uint64_t start_time = measure_read_barrier_slow_path_ ? NanoTime() : 0u;
-  mirror::Object* ret = Mark(from_ref);
+  mirror::Object* ret = Mark(self, from_ref);
   if (measure_read_barrier_slow_path_) {
     rb_slow_path_ns_.fetch_add(NanoTime() - start_time, std::memory_order_relaxed);
   }
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index a00dbb5..f1e7e2f 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -112,7 +112,8 @@
   }
   template<bool kGrayImmuneObject = true, bool kFromGCThread = false>
   // Mark object `from_ref`, copying it to the to-space if needed.
-  ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref,
+  ALWAYS_INLINE mirror::Object* Mark(Thread* const self,
+                                     mirror::Object* from_ref,
                                      mirror::Object* holder = nullptr,
                                      MemberOffset offset = MemberOffset(0))
       REQUIRES_SHARED(Locks::mutator_lock_)
@@ -144,9 +145,11 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
-  void PushOntoMarkStack(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_)
+  void PushOntoMarkStack(Thread* const self, mirror::Object* obj)
+      REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_);
-  mirror::Object* Copy(mirror::Object* from_ref,
+  mirror::Object* Copy(Thread* const self,
+                       mirror::Object* from_ref,
                        mirror::Object* holder,
                        MemberOffset offset)
       REQUIRES_SHARED(Locks::mutator_lock_)
@@ -162,7 +165,7 @@
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
   template<bool kGrayImmuneObject>
-  void MarkRoot(mirror::CompressedReference<mirror::Object>* root)
+  void MarkRoot(Thread* const self, mirror::CompressedReference<mirror::Object>* root)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
   virtual void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
@@ -220,10 +223,10 @@
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
   void MarkZygoteLargeObjects()
       REQUIRES_SHARED(Locks::mutator_lock_);
-  void FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size)
+  void FillWithDummyObject(Thread* const self, mirror::Object* dummy_obj, size_t byte_size)
       REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  mirror::Object* AllocateInSkippedBlock(size_t alloc_size)
+  mirror::Object* AllocateInSkippedBlock(Thread* const self, size_t alloc_size)
       REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
   void CheckEmptyMarkStack() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
@@ -253,25 +256,30 @@
   void DisableMarking() REQUIRES_SHARED(Locks::mutator_lock_);
   void IssueDisableMarkingCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_);
   void ExpandGcMarkStack() REQUIRES_SHARED(Locks::mutator_lock_);
-  mirror::Object* MarkNonMoving(mirror::Object* from_ref,
+  mirror::Object* MarkNonMoving(Thread* const self,
+                                mirror::Object* from_ref,
                                 mirror::Object* holder = nullptr,
                                 MemberOffset offset = MemberOffset(0))
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
-  ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegion(mirror::Object* from_ref,
+  ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegion(Thread* const self,
+      mirror::Object* from_ref,
       accounting::SpaceBitmap<kObjectAlignment>* bitmap)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
   template<bool kGrayImmuneObject>
-  ALWAYS_INLINE mirror::Object* MarkImmuneSpace(mirror::Object* from_ref)
+  ALWAYS_INLINE mirror::Object* MarkImmuneSpace(Thread* const self,
+                                                mirror::Object* from_ref)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!immune_gray_stack_lock_);
-  void PushOntoFalseGrayStack(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_)
+  void PushOntoFalseGrayStack(Thread* const self, mirror::Object* obj)
+      REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_);
   void ProcessFalseGrayStack() REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_);
   void ScanImmuneObject(mirror::Object* obj)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
-  mirror::Object* MarkFromReadBarrierWithMeasurements(mirror::Object* from_ref)
+  mirror::Object* MarkFromReadBarrierWithMeasurements(Thread* const self,
+                                                      mirror::Object* from_ref)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
   void DumpPerformanceInfo(std::ostream& os) OVERRIDE REQUIRES(!rb_slow_path_histogram_lock_);
@@ -330,8 +338,12 @@
   bool weak_ref_access_enabled_ GUARDED_BY(Locks::thread_list_lock_);
 
   // How many objects and bytes we moved. Used for accounting.
-  Atomic<size_t> bytes_moved_;
-  Atomic<size_t> objects_moved_;
+  // GC thread moves many more objects than mutators.
+  // Therefore, we separate the two to avoid CAS.
+  Atomic<size_t> bytes_moved_;  // Used by mutators
+  Atomic<size_t> objects_moved_;  // Used by mutators
+  size_t bytes_moved_gc_thread_;  // Used by GC
+  size_t objects_moved_gc_thread_;  // Used by GC
   Atomic<uint64_t> cumulative_bytes_moved_;
   Atomic<uint64_t> cumulative_objects_moved_;
 
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index d7f33d5..d752805 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -154,8 +154,16 @@
     return;
   }
   // Don't stub Proxy.<init>. Note that the Proxy class itself is not a proxy class.
-  if (method->IsConstructor() &&
-      method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;")) {
+  // TODO We should remove the need for this since it means we cannot always correctly detect calls
+  // to Proxy.<init>
+  // Annoyingly this can be called before we have actually initialized WellKnownClasses so therefore
+  // we also need to check this based on the declaring-class descriptor. The check is valid because
+  // Proxy only has a single constructor.
+  ArtMethod* well_known_proxy_init = jni::DecodeArtMethod(
+      WellKnownClasses::java_lang_reflect_Proxy_init);
+  if ((LIKELY(well_known_proxy_init != nullptr) && UNLIKELY(method == well_known_proxy_init)) ||
+      UNLIKELY(method->IsConstructor() &&
+               method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;"))) {
     return;
   }
   const void* new_quick_code;
@@ -354,7 +362,7 @@
       }
       uint32_t dex_pc = visitor.dex_pcs_.back();
       visitor.dex_pcs_.pop_back();
-      if (!isi->interpreter_entry_) {
+      if (!isi->interpreter_entry_ && !isi->method_->IsRuntimeMethod()) {
         instrumentation->MethodEnterEvent(thread, (*isi).this_object_, (*isi).method_, dex_pc);
       }
     }
@@ -785,7 +793,13 @@
       if (class_linker->IsQuickResolutionStub(quick_code) ||
           class_linker->IsQuickToInterpreterBridge(quick_code)) {
         new_quick_code = quick_code;
-      } else if (entry_exit_stubs_installed_) {
+      } else if (entry_exit_stubs_installed_ &&
+                 // We need to make sure not to replace anything that InstallStubsForMethod
+                 // wouldn't. Specifically we cannot stub out Proxy.<init> since subtypes copy the
+                 // implementation directly and this will confuse the instrumentation trampolines.
+                 // TODO We should remove the need for this since it makes it impossible to profile
+                 // Proxy.<init> correctly in all cases.
+                 method != jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Proxy_init)) {
         new_quick_code = GetQuickInstrumentationEntryPoint();
       } else {
         new_quick_code = quick_code;
@@ -912,7 +926,7 @@
     }
 
     // If there is no deoptimized method left, we can restore the stack of each thread.
-    if (empty) {
+    if (empty && !entry_exit_stubs_installed_) {
       MutexLock mu(self, *Locks::thread_list_lock_);
       Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, this);
       instrumentation_stubs_installed_ = false;
diff --git a/runtime/oat.h b/runtime/oat.h
index 40f4edd..22c6a39 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  // Last oat version changed reason: compiler support invoke-custom
-  static constexpr uint8_t kOatVersion[] = { '1', '4', '8', '\0' };
+  // Last oat version changed reason: Add Kind column to stack maps.
+  static constexpr uint8_t kOatVersion[] = { '1', '4', '9', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index 946ea01..36dea60 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -23,11 +23,24 @@
 #include "mirror/field-inl.h"
 #include "proxy_test.h"
 #include "scoped_thread_state_change-inl.h"
+#include "well_known_classes.h"
 
 namespace art {
 namespace proxy_test {
 
-class ProxyTest : public CommonRuntimeTest {};
+class ProxyTest : public CommonRuntimeTest {
+ protected:
+  void SetUp() OVERRIDE {
+    CommonRuntimeTest::SetUp();
+    // The creation of a Proxy class uses WellKnownClasses. These are not normally initialized by
+    // CommonRuntimeTest so we need to do that now.
+    WellKnownClasses::Clear();
+    WellKnownClasses::Init(art::Thread::Current()->GetJniEnv());
+    // Since we aren't actually calling any of the native functions we can just immediately call
+    // LateInit after calling Init.
+    WellKnownClasses::LateInit(art::Thread::Current()->GetJniEnv());
+  }
+};
 
 // Creates a proxy class and check ClassHelper works correctly.
 TEST_F(ProxyTest, ProxyClassHelper) {
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 2188cdc..a181bfe 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -849,7 +849,8 @@
         uint8_t* return_pc_addr = reinterpret_cast<uint8_t*>(cur_quick_frame_) + return_pc_offset;
         uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr);
 
-        if (UNLIKELY(exit_stubs_installed)) {
+        if (UNLIKELY(exit_stubs_installed ||
+                     reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == return_pc)) {
           // While profiling, the return pc is restored from the side stack, except when walking
           // the stack for an exception where the side stack will be unwound in VisitFrame.
           if (reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == return_pc) {
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index aa19f09..8af73e9 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -119,16 +119,23 @@
  * - Knowing the inlining information,
  * - Knowing the values of dex registers.
  */
-class StackMap : public BitTable<7>::Accessor {
+class StackMap : public BitTable<8>::Accessor {
  public:
+  enum Kind {
+    Default = -1,
+    Catch = 0,
+    OSR = 1,
+    Debug = 2,
+  };
   BIT_TABLE_HEADER()
-  BIT_TABLE_COLUMN(0, PackedNativePc)
-  BIT_TABLE_COLUMN(1, DexPc)
-  BIT_TABLE_COLUMN(2, RegisterMaskIndex)
-  BIT_TABLE_COLUMN(3, StackMaskIndex)
-  BIT_TABLE_COLUMN(4, InlineInfoIndex)
-  BIT_TABLE_COLUMN(5, DexRegisterMaskIndex)
-  BIT_TABLE_COLUMN(6, DexRegisterMapIndex)
+  BIT_TABLE_COLUMN(0, Kind)
+  BIT_TABLE_COLUMN(1, PackedNativePc)
+  BIT_TABLE_COLUMN(2, DexPc)
+  BIT_TABLE_COLUMN(3, RegisterMaskIndex)
+  BIT_TABLE_COLUMN(4, StackMaskIndex)
+  BIT_TABLE_COLUMN(5, InlineInfoIndex)
+  BIT_TABLE_COLUMN(6, DexRegisterMaskIndex)
+  BIT_TABLE_COLUMN(7, DexRegisterMapIndex)
 
   ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const {
     return UnpackNativePc(Get<kPackedNativePc>(), instruction_set);
@@ -377,19 +384,18 @@
   StackMap GetStackMapForDexPc(uint32_t dex_pc) const {
     for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
       StackMap stack_map = GetStackMapAt(i);
-      if (stack_map.GetDexPc() == dex_pc) {
+      if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() != StackMap::Kind::Debug) {
         return stack_map;
       }
     }
     return StackMap();
   }
 
-  // Searches the stack map list backwards because catch stack maps are stored
-  // at the end.
+  // Searches the stack map list backwards because catch stack maps are stored at the end.
   StackMap GetCatchStackMapForDexPc(uint32_t dex_pc) const {
     for (size_t i = GetNumberOfStackMaps(); i > 0; --i) {
       StackMap stack_map = GetStackMapAt(i - 1);
-      if (stack_map.GetDexPc() == dex_pc) {
+      if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() == StackMap::Kind::Catch) {
         return stack_map;
       }
     }
@@ -397,41 +403,26 @@
   }
 
   StackMap GetOsrStackMapForDexPc(uint32_t dex_pc) const {
-    size_t e = GetNumberOfStackMaps();
-    if (e == 0) {
-      // There cannot be OSR stack map if there is no stack map.
-      return StackMap();
-    }
-    // Walk over all stack maps. If two consecutive stack maps are identical, then we
-    // have found a stack map suitable for OSR.
-    for (size_t i = 0; i < e - 1; ++i) {
+    for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
       StackMap stack_map = GetStackMapAt(i);
-      if (stack_map.GetDexPc() == dex_pc) {
-        StackMap other = GetStackMapAt(i + 1);
-        if (other.GetDexPc() == dex_pc &&
-            other.GetNativePcOffset(kRuntimeISA) ==
-                stack_map.GetNativePcOffset(kRuntimeISA)) {
-          if (i < e - 2) {
-            // Make sure there are not three identical stack maps following each other.
-            DCHECK_NE(
-                stack_map.GetNativePcOffset(kRuntimeISA),
-                GetStackMapAt(i + 2).GetNativePcOffset(kRuntimeISA));
-          }
-          return stack_map;
-        }
+      if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() == StackMap::Kind::OSR) {
+        return stack_map;
       }
     }
     return StackMap();
   }
 
-  StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) const {
+  StackMap GetStackMapForNativePcOffset(uint32_t pc, InstructionSet isa = kRuntimeISA) const {
     // TODO: Safepoint stack maps are sorted by native_pc_offset but catch stack
     //       maps are not. If we knew that the method does not have try/catch,
     //       we could do binary search.
     for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
       StackMap stack_map = GetStackMapAt(i);
-      if (stack_map.GetNativePcOffset(kRuntimeISA) == native_pc_offset) {
-        return stack_map;
+      if (stack_map.GetNativePcOffset(isa) == pc) {
+        StackMap::Kind kind = static_cast<StackMap::Kind>(stack_map.GetKind());
+        if (kind == StackMap::Kind::Default || kind == StackMap::Kind::OSR) {
+          return stack_map;
+        }
       }
     }
     return StackMap();
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index c64e7bb..206418f 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -26,6 +26,7 @@
 #include "base/enums.h"
 #include "class_linker.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "hidden_api.h"
 #include "jni/jni_internal.h"
 #include "mirror/class.h"
@@ -98,6 +99,7 @@
 jmethodID WellKnownClasses::java_lang_ref_FinalizerReference_add;
 jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add;
 jmethodID WellKnownClasses::java_lang_reflect_Parameter_init;
+jmethodID WellKnownClasses::java_lang_reflect_Proxy_init;
 jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke;
 jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
 jmethodID WellKnownClasses::java_lang_Short_valueOf;
@@ -418,6 +420,14 @@
       CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad",
                   "(Ljava/lang/String;Ljava/lang/ClassLoader;)"
                       "Ljava/lang/String;");
+  java_lang_reflect_Proxy_init =
+    CacheMethod(env, java_lang_reflect_Proxy, false, "<init>",
+                "(Ljava/lang/reflect/InvocationHandler;)V");
+  // This invariant is important since otherwise we will have the entire proxy invoke system
+  // confused.
+  DCHECK_NE(
+      jni::DecodeArtMethod(java_lang_reflect_Proxy_init)->GetEntryPointFromQuickCompiledCode(),
+      GetQuickInstrumentationEntryPoint());
   java_lang_reflect_Proxy_invoke =
     CacheMethod(env, java_lang_reflect_Proxy, true, "invoke",
                 "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;"
@@ -484,6 +494,7 @@
   java_lang_ref_FinalizerReference_add = nullptr;
   java_lang_ref_ReferenceQueue_add = nullptr;
   java_lang_reflect_Parameter_init = nullptr;
+  java_lang_reflect_Proxy_init = nullptr;
   java_lang_reflect_Proxy_invoke = nullptr;
   java_lang_Runtime_nativeLoad = nullptr;
   java_lang_Short_valueOf = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c81062f..ce5ab1d 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -108,6 +108,7 @@
   static jmethodID java_lang_ref_FinalizerReference_add;
   static jmethodID java_lang_ref_ReferenceQueue_add;
   static jmethodID java_lang_reflect_Parameter_init;
+  static jmethodID java_lang_reflect_Proxy_init;
   static jmethodID java_lang_reflect_Proxy_invoke;
   static jmethodID java_lang_Runtime_nativeLoad;
   static jmethodID java_lang_Short_valueOf;
diff --git a/test/004-ThreadStress/run b/test/004-ThreadStress/run
index 8004036..067e0d0 100755
--- a/test/004-ThreadStress/run
+++ b/test/004-ThreadStress/run
@@ -15,7 +15,29 @@
 # limitations under the License.
 
 # Enable lock contention logging.
-${RUN} --runtime-option -Xlockprofthreshold:10 "${@}"
+if [[ "x$ART_DEFAULT_GC_TYPE" = xGSS ]]; then
+  # NonMovingAlloc operations fail an assertion with the Generational
+  # Semi-Space (GSS) collector (see b/72738921); disable them for now
+  # by explicitly assigning frequencies to operations when the GSS
+  # collector is used.
+  #
+  # Note: The trick to use command substitution to have comments within
+  # a multi-line command is from https://stackoverflow.com/a/12797512.
+  ${RUN} --runtime-option -Xlockprofthreshold:10 "${@}" Main \
+    -oom:0.005           `#   1/200` \
+    -sigquit:0.095       `#  19/200` \
+    -alloc:0.225         `#  45/200` \
+    -largealloc:0.05     `#  10/200` \
+    -nonmovingalloc:0.0  `#   0/200` \
+    -stacktrace:0.1      `#  20/200` \
+    -exit:0.225          `#  45/200` \
+    -sleep:0.125         `#  25/200` \
+    -timedwait:0.05      `#  10/200` \
+    -wait:0.075          `#  15/200` \
+    -queuedwait:0.05     `#  10/200`
+else
+  ${RUN} --runtime-option -Xlockprofthreshold:10 "${@}"
+fi
 return_status1=$?
 
 # Run locks-only mode with stack-dump lock profiling. Reduce the number of total operations from
diff --git a/test/004-ThreadStress/src-art/Main.java b/test/004-ThreadStress/src-art/Main.java
index a142934..3a89f4f 100644
--- a/test/004-ThreadStress/src-art/Main.java
+++ b/test/004-ThreadStress/src-art/Main.java
@@ -315,11 +315,9 @@
         Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
         frequencyMap.put(new OOM(), 0.005);                   //   1/200
         frequencyMap.put(new SigQuit(), 0.095);               //  19/200
-        frequencyMap.put(new Alloc(), 0.225);                 //  45/200
+        frequencyMap.put(new Alloc(), 0.2);                   //  40/200
         frequencyMap.put(new LargeAlloc(), 0.05);             //  10/200
-        // TODO: NonMovingAlloc operations fail an assertion with the
-        // GSS collector (see b/72738921); disable them for now.
-        frequencyMap.put(new NonMovingAlloc(), 0.0);          //   0/200
+        frequencyMap.put(new NonMovingAlloc(), 0.025);        //   5/200
         frequencyMap.put(new StackTrace(), 0.1);              //  20/200
         frequencyMap.put(new Exit(), 0.225);                  //  45/200
         frequencyMap.put(new Sleep(), 0.125);                 //  25/200
@@ -379,6 +377,8 @@
             op = new Alloc();
         } else if (split[0].equals("-largealloc")) {
             op = new LargeAlloc();
+        } else if (split[0].equals("-nonmovingalloc")) {
+            op = new NonMovingAlloc();
         } else if (split[0].equals("-stacktrace")) {
             op = new StackTrace();
         } else if (split[0].equals("-exit")) {
diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java
index 40e3778..9e714f5 100644
--- a/test/458-checker-instruct-simplification/src/Main.java
+++ b/test/458-checker-instruct-simplification/src/Main.java
@@ -2458,6 +2458,77 @@
     return (byte)((int)(((long)(b & 0xff)) & 255L));
   }
 
+  /// CHECK-START: int Main.$noinline$emptyStringIndexOf(int) instruction_simplifier (before)
+  /// CHECK-DAG:      <<Arg:i\d+>>      ParameterValue
+  /// CHECK-DAG:      <<Empty:l\d+>>    LoadString
+  /// CHECK-DAG:      <<Equals:i\d+>>   InvokeVirtual [<<Empty>>,<<Arg>>] intrinsic:StringIndexOf
+  /// CHECK-DAG:                        Return [<<Equals>>]
+
+  /// CHECK-START: int Main.$noinline$emptyStringIndexOf(int) instruction_simplifier (after)
+  /// CHECK-NOT:                        InvokeVirtual
+
+  /// CHECK-START: int Main.$noinline$emptyStringIndexOf(int) instruction_simplifier (after)
+  /// CHECK-DAG:      <<Minus1:i\d+>>   IntConstant -1
+  /// CHECK-DAG:                        Return [<<Minus1>>]
+  public static int $noinline$emptyStringIndexOf(int ch) {
+    return "".indexOf(ch);
+  }
+
+  /// CHECK-START: int Main.$noinline$emptyStringIndexOfAfter(int, int) instruction_simplifier (before)
+  /// CHECK-DAG:      <<Arg1:i\d+>>     ParameterValue
+  /// CHECK-DAG:      <<Arg2:i\d+>>     ParameterValue
+  /// CHECK-DAG:      <<Empty:l\d+>>    LoadString
+  /// CHECK-DAG:      <<Equals:i\d+>>   InvokeVirtual [<<Empty>>,<<Arg1>>,<<Arg2>>] intrinsic:StringIndexOfAfter
+  /// CHECK-DAG:                        Return [<<Equals>>]
+
+  /// CHECK-START: int Main.$noinline$emptyStringIndexOfAfter(int, int) instruction_simplifier (after)
+  /// CHECK-NOT:                        InvokeVirtual
+
+  /// CHECK-START: int Main.$noinline$emptyStringIndexOfAfter(int, int) instruction_simplifier (after)
+  /// CHECK-DAG:      <<Minus1:i\d+>>   IntConstant -1
+  /// CHECK-DAG:                        Return [<<Minus1>>]
+  public static int $noinline$emptyStringIndexOfAfter(int ch, int fromIndex) {
+    return "".indexOf(ch, fromIndex);
+  }
+
+  /// CHECK-START: int Main.$noinline$singleCharStringIndexOf(int) instruction_simplifier (before)
+  /// CHECK-DAG:      <<Arg:i\d+>>      ParameterValue
+  /// CHECK-DAG:      <<Empty:l\d+>>    LoadString
+  /// CHECK-DAG:      <<Equals:i\d+>>   InvokeVirtual [<<Empty>>,<<Arg>>] intrinsic:StringIndexOf
+  /// CHECK-DAG:                        Return [<<Equals>>]
+
+  /// CHECK-START: int Main.$noinline$singleCharStringIndexOf(int) instruction_simplifier (after)
+  /// CHECK-NOT:                        InvokeVirtual
+
+  /// CHECK-START: int Main.$noinline$singleCharStringIndexOf(int) instruction_simplifier (after)
+  /// CHECK-DAG:      <<Arg:i\d+>>      ParameterValue
+  /// CHECK-DAG:      <<x:i\d+>>        IntConstant 120
+  /// CHECK-DAG:      <<Zero:i\d+>>     IntConstant 0
+  /// CHECK-DAG:      <<Minus1:i\d+>>   IntConstant -1
+  /// CHECK-DAG:      <<Eq:z\d+>>       Equal [<<Arg>>,<<x>>]
+  /// CHECK-DAG:      <<Select:i\d+>>   Select [<<Minus1>>,<<Zero>>,<<Eq>>]
+  /// CHECK-DAG:                        Return [<<Select>>]
+  public static int $noinline$singleCharStringIndexOf(int ch) {
+    return "x".indexOf(ch);
+  }
+
+  /// CHECK-START: int Main.$noinline$singleCharStringIndexOfAfter(int, int) instruction_simplifier (before)
+  /// CHECK-DAG:      <<Arg1:i\d+>>     ParameterValue
+  /// CHECK-DAG:      <<Arg2:i\d+>>     ParameterValue
+  /// CHECK-DAG:      <<Empty:l\d+>>    LoadString
+  /// CHECK-DAG:      <<Equals:i\d+>>   InvokeVirtual [<<Empty>>,<<Arg1>>,<<Arg2>>] intrinsic:StringIndexOfAfter
+  /// CHECK-DAG:                        Return [<<Equals>>]
+
+  /// CHECK-START: int Main.$noinline$singleCharStringIndexOfAfter(int, int) instruction_simplifier (after)
+  /// CHECK-DAG:      <<Arg1:i\d+>>     ParameterValue
+  /// CHECK-DAG:      <<Arg2:i\d+>>     ParameterValue
+  /// CHECK-DAG:      <<Empty:l\d+>>    LoadString
+  /// CHECK-DAG:      <<Equals:i\d+>>   InvokeVirtual [<<Empty>>,<<Arg1>>,<<Arg2>>] intrinsic:StringIndexOfAfter
+  /// CHECK-DAG:                        Return [<<Equals>>]
+  public static int $noinline$singleCharStringIndexOfAfter(int ch, int fromIndex) {
+    return "x".indexOf(ch, fromIndex);  // Not simplified.
+  }
+
   public static void main(String[] args) throws Exception {
     Class smaliTests2 = Class.forName("SmaliTests2");
     Method $noinline$XorAllOnes = smaliTests2.getMethod("$noinline$XorAllOnes", int.class);
@@ -2709,6 +2780,19 @@
     assertIntEquals(1, (int)$noinline$bug68142795Boolean.invoke(null, true));
     assertIntEquals(0x7f, $noinline$bug68142795Elaborate((byte) 0x7f));
     assertIntEquals((byte) 0x80, $noinline$bug68142795Elaborate((byte) 0x80));
+
+    assertIntEquals(-1, $noinline$emptyStringIndexOf('a'));
+    assertIntEquals(-1, $noinline$emptyStringIndexOf('Z'));
+    assertIntEquals(-1, $noinline$emptyStringIndexOfAfter('a', 0));
+    assertIntEquals(-1, $noinline$emptyStringIndexOfAfter('Z', -1));
+
+    assertIntEquals(-1, $noinline$singleCharStringIndexOf('a'));
+    assertIntEquals(0, $noinline$singleCharStringIndexOf('x'));
+    assertIntEquals(-1, $noinline$singleCharStringIndexOf('Z'));
+    assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('a', 0));
+    assertIntEquals(0, $noinline$singleCharStringIndexOfAfter('x', -1));
+    assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('x', 1));
+    assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('Z', -1));
   }
 
   private static boolean $inline$true() { return true; }
diff --git a/test/566-checker-signum/build b/test/566-checker-signum/build
deleted file mode 100644
index 10ffcc5..0000000
--- a/test/566-checker-signum/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/566-checker-signum/smali/Main2.smali b/test/566-checker-signum/smali/Main2.smali
new file mode 100644
index 0000000..d99ad86
--- /dev/null
+++ b/test/566-checker-signum/smali/Main2.smali
@@ -0,0 +1,83 @@
+# 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.
+
+.class public LMain2;
+.super Ljava/lang/Object;
+
+## CHECK-START: int Main2.signBoolean(boolean) intrinsics_recognition (after)
+## CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
+## CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
+## CHECK-DAG:     <<One:i\d+>>    IntConstant 1
+## CHECK-DAG:     <<Phi:i\d+>>    Phi [<<One>>,<<Zero>>]
+## CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect [<<Phi>>,<<Method>>] intrinsic:IntegerSignum
+## CHECK-DAG:                     Return [<<Result>>]
+
+## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier (after)
+## CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
+## CHECK-DAG:     <<One:i\d+>>    IntConstant 1
+## CHECK-DAG:     <<Phi:i\d+>>    Phi [<<One>>,<<Zero>>]
+## CHECK-DAG:     <<Result:i\d+>> Compare [<<Phi>>,<<Zero>>]
+## CHECK-DAG:                     Return [<<Result>>]
+
+## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier (after)
+## CHECK-NOT:                     InvokeStaticOrDirect
+
+## CHECK-START: int Main2.signBoolean(boolean) select_generator (after)
+## CHECK-DAG:     <<Arg:z\d+>>    ParameterValue
+## CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
+## CHECK-DAG:     <<One:i\d+>>    IntConstant 1
+## CHECK-DAG:     <<Sel:i\d+>>    Select [<<Zero>>,<<One>>,<<Arg>>]
+## CHECK-DAG:     <<Result:i\d+>> Compare [<<Sel>>,<<Zero>>]
+## CHECK-DAG:                     Return [<<Result>>]
+
+## CHECK-START: int Main2.signBoolean(boolean) select_generator (after)
+## CHECK-NOT:                     Phi
+
+## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier$after_bce (after)
+## CHECK-DAG:     <<Arg:z\d+>>    ParameterValue
+## CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
+## CHECK-DAG:     <<Result:i\d+>> Compare [<<Arg>>,<<Zero>>]
+## CHECK-DAG:                     Return [<<Result>>]
+
+## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT:                     Select
+
+# Original java source:
+#
+#     private static int signBoolean(boolean x) {
+#       return Integer.signum(x ? 1 : 0);
+#     }
+
+.method public static signBoolean(Z)I
+    .registers 2
+    .param p0, "x"    # Z
+
+    .prologue
+    .line 58
+    if-eqz p0, :cond_8
+
+    const/4 v0, 0x1
+
+    :goto_3
+    invoke-static {v0}, Ljava/lang/Integer;->signum(I)I
+
+    move-result v0
+
+    return v0
+
+    :cond_8
+    const/4 v0, 0x0
+
+    goto :goto_3
+.end method
diff --git a/test/566-checker-signum/src-art/Main.java b/test/566-checker-signum/src-art/Main.java
new file mode 100644
index 0000000..f1e1e1b
--- /dev/null
+++ b/test/566-checker-signum/src-art/Main.java
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+
+  /// CHECK-START: int Main.signByte(byte) intrinsics_recognition (after)
+  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+  /// CHECK-DAG:                     Return [<<Result>>]
+
+  /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after)
+  /// CHECK-DAG:     <<Result:i\d+>> Compare
+  /// CHECK-DAG:                     Return [<<Result>>]
+
+  /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after)
+  /// CHECK-NOT:                     InvokeStaticOrDirect
+
+  private static int signByte(byte x) {
+    return Integer.signum(x);
+  }
+
+  /// CHECK-START: int Main.signShort(short) intrinsics_recognition (after)
+  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+  /// CHECK-DAG:                     Return [<<Result>>]
+
+  /// CHECK-START: int Main.signShort(short) instruction_simplifier (after)
+  /// CHECK-DAG:     <<Result:i\d+>> Compare
+  /// CHECK-DAG:                     Return [<<Result>>]
+
+  /// CHECK-START: int Main.signShort(short) instruction_simplifier (after)
+  /// CHECK-NOT:                     InvokeStaticOrDirect
+
+  private static int signShort(short x) {
+    return Integer.signum(x);
+  }
+
+  /// CHECK-START: int Main.signChar(char) intrinsics_recognition (after)
+  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+  /// CHECK-DAG:                     Return [<<Result>>]
+
+  /// CHECK-START: int Main.signChar(char) instruction_simplifier (after)
+  /// CHECK-DAG:     <<Result:i\d+>> Compare
+  /// CHECK-DAG:                     Return [<<Result>>]
+
+  /// CHECK-START: int Main.signChar(char) instruction_simplifier (after)
+  /// CHECK-NOT:                     InvokeStaticOrDirect
+
+  private static int signChar(char x) {
+    return Integer.signum(x);
+  }
+
+  /// CHECK-START: int Main.signInt(int) intrinsics_recognition (after)
+  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+  /// CHECK-DAG:                     Return [<<Result>>]
+
+  /// CHECK-START: int Main.signInt(int) instruction_simplifier (after)
+  /// CHECK-DAG:     <<Result:i\d+>> Compare
+  /// CHECK-DAG:                     Return [<<Result>>]
+
+  /// CHECK-START: int Main.signInt(int) instruction_simplifier (after)
+  /// CHECK-NOT:                     InvokeStaticOrDirect
+
+  private static int signInt(int x) {
+    return Integer.signum(x);
+  }
+
+  /// CHECK-START: int Main.signLong(long) intrinsics_recognition (after)
+  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongSignum
+  /// CHECK-DAG:                     Return [<<Result>>]
+
+  /// CHECK-START: int Main.signLong(long) instruction_simplifier (after)
+  /// CHECK-DAG:     <<Result:i\d+>> Compare
+  /// CHECK-DAG:                     Return [<<Result>>]
+
+  /// CHECK-START: int Main.signLong(long) instruction_simplifier (after)
+  /// CHECK-NOT:                     InvokeStaticOrDirect
+
+  private static int signLong(long x) {
+    return Long.signum(x);
+  }
+
+
+  public static void testSignBoolean() throws Exception {
+    Method signBoolean = Class.forName("Main2").getMethod("signBoolean", boolean.class);
+    expectEquals(0, (int)signBoolean.invoke(null, false));
+    expectEquals(1, (int)signBoolean.invoke(null, true));
+  }
+
+  public static void testSignByte() {
+    expectEquals(-1, signByte((byte)Byte.MIN_VALUE));
+    expectEquals(-1, signByte((byte)-64));
+    expectEquals(-1, signByte((byte)-1));
+    expectEquals(0, signByte((byte)0));
+    expectEquals(1, signByte((byte)1));
+    expectEquals(1, signByte((byte)64));
+    expectEquals(1, signByte((byte)Byte.MAX_VALUE));
+  }
+
+  public static void testSignShort() {
+    expectEquals(-1, signShort((short)Short.MIN_VALUE));
+    expectEquals(-1, signShort((short)-12345));
+    expectEquals(-1, signShort((short)-1));
+    expectEquals(0, signShort((short)0));
+    expectEquals(1, signShort((short)1));
+    expectEquals(1, signShort((short)12345));
+    expectEquals(1, signShort((short)Short.MAX_VALUE));
+  }
+
+  public static void testSignChar() {
+    expectEquals(0, signChar((char)0));
+    expectEquals(1, signChar((char)1));
+    expectEquals(1, signChar((char)12345));
+    expectEquals(1, signChar((char)Character.MAX_VALUE));
+  }
+
+  public static void testSignInt() {
+    expectEquals(-1, signInt(Integer.MIN_VALUE));
+    expectEquals(-1, signInt(-12345));
+    expectEquals(-1, signInt(-1));
+    expectEquals(0, signInt(0));
+    expectEquals(1, signInt(1));
+    expectEquals(1, signInt(12345));
+    expectEquals(1, signInt(Integer.MAX_VALUE));
+
+    for (int i = -11; i <= 11; i++) {
+      int expected = 0;
+      if (i < 0) expected = -1;
+      else if (i > 0) expected = 1;
+      expectEquals(expected, signInt(i));
+    }
+  }
+
+  public static void testSignLong() {
+    expectEquals(-1, signLong(Long.MIN_VALUE));
+    expectEquals(-1, signLong(-12345L));
+    expectEquals(-1, signLong(-1L));
+    expectEquals(0, signLong(0L));
+    expectEquals(1, signLong(1L));
+    expectEquals(1, signLong(12345L));
+    expectEquals(1, signLong(Long.MAX_VALUE));
+
+    expectEquals(-1, signLong(0x800000007FFFFFFFL));
+    expectEquals(-1, signLong(0x80000000FFFFFFFFL));
+    expectEquals(1, signLong(0x000000007FFFFFFFL));
+    expectEquals(1, signLong(0x00000000FFFFFFFFL));
+    expectEquals(1, signLong(0x7FFFFFFF7FFFFFFFL));
+    expectEquals(1, signLong(0x7FFFFFFFFFFFFFFFL));
+
+    for (long i = -11L; i <= 11L; i++) {
+      int expected = 0;
+      if (i < 0) expected = -1;
+      else if (i > 0) expected = 1;
+      expectEquals(expected, signLong(i));
+    }
+
+    for (long i = Long.MIN_VALUE; i <= Long.MIN_VALUE + 11L; i++) {
+      expectEquals(-1, signLong(i));
+    }
+
+    for (long i = Long.MAX_VALUE; i >= Long.MAX_VALUE - 11L; i--) {
+      expectEquals(1, signLong(i));
+    }
+  }
+
+
+  public static void main(String args[]) throws Exception {
+    testSignBoolean();
+    testSignByte();
+    testSignShort();
+    testSignChar();
+    testSignInt();
+    testSignLong();
+
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+}
diff --git a/test/566-checker-signum/src/Main.java b/test/566-checker-signum/src/Main.java
index 7fc9e84..fa8e5cd 100644
--- a/test/566-checker-signum/src/Main.java
+++ b/test/566-checker-signum/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,222 +14,9 @@
  * limitations under the License.
  */
 
+// This file is just for running on the RI as the test is ART specific.
 public class Main {
-
-  /// CHECK-START: int Main.signBoolean(boolean) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
-  /// CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
-  /// CHECK-DAG:     <<One:i\d+>>    IntConstant 1
-  /// CHECK-DAG:     <<Phi:i\d+>>    Phi [<<One>>,<<Zero>>]
-  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect [<<Phi>>,<<Method>>] intrinsic:IntegerSignum
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier (after)
-  /// CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
-  /// CHECK-DAG:     <<One:i\d+>>    IntConstant 1
-  /// CHECK-DAG:     <<Phi:i\d+>>    Phi [<<One>>,<<Zero>>]
-  /// CHECK-DAG:     <<Result:i\d+>> Compare [<<Phi>>,<<Zero>>]
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier (after)
-  /// CHECK-NOT:                     InvokeStaticOrDirect
-
-  /// CHECK-START: int Main.signBoolean(boolean) select_generator (after)
-  /// CHECK-DAG:     <<Arg:z\d+>>    ParameterValue
-  /// CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
-  /// CHECK-DAG:     <<One:i\d+>>    IntConstant 1
-  /// CHECK-DAG:     <<Sel:i\d+>>    Select [<<Zero>>,<<One>>,<<Arg>>]
-  /// CHECK-DAG:     <<Result:i\d+>> Compare [<<Sel>>,<<Zero>>]
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signBoolean(boolean) select_generator (after)
-  /// CHECK-NOT:                     Phi
-
-  /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier$after_bce (after)
-  /// CHECK-DAG:     <<Arg:z\d+>>    ParameterValue
-  /// CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
-  /// CHECK-DAG:     <<Result:i\d+>> Compare [<<Arg>>,<<Zero>>]
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier$after_bce (after)
-  /// CHECK-NOT:                     Select
-
-  private static int signBoolean(boolean x) {
-    return Integer.signum(x ? 1 : 0);
-  }
-
-  /// CHECK-START: int Main.signByte(byte) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after)
-  /// CHECK-DAG:     <<Result:i\d+>> Compare
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after)
-  /// CHECK-NOT:                     InvokeStaticOrDirect
-
-  private static int signByte(byte x) {
-    return Integer.signum(x);
-  }
-
-  /// CHECK-START: int Main.signShort(short) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signShort(short) instruction_simplifier (after)
-  /// CHECK-DAG:     <<Result:i\d+>> Compare
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signShort(short) instruction_simplifier (after)
-  /// CHECK-NOT:                     InvokeStaticOrDirect
-
-  private static int signShort(short x) {
-    return Integer.signum(x);
-  }
-
-  /// CHECK-START: int Main.signChar(char) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signChar(char) instruction_simplifier (after)
-  /// CHECK-DAG:     <<Result:i\d+>> Compare
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signChar(char) instruction_simplifier (after)
-  /// CHECK-NOT:                     InvokeStaticOrDirect
-
-  private static int signChar(char x) {
-    return Integer.signum(x);
-  }
-
-  /// CHECK-START: int Main.signInt(int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signInt(int) instruction_simplifier (after)
-  /// CHECK-DAG:     <<Result:i\d+>> Compare
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signInt(int) instruction_simplifier (after)
-  /// CHECK-NOT:                     InvokeStaticOrDirect
-
-  private static int signInt(int x) {
-    return Integer.signum(x);
-  }
-
-  /// CHECK-START: int Main.signLong(long) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongSignum
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signLong(long) instruction_simplifier (after)
-  /// CHECK-DAG:     <<Result:i\d+>> Compare
-  /// CHECK-DAG:                     Return [<<Result>>]
-
-  /// CHECK-START: int Main.signLong(long) instruction_simplifier (after)
-  /// CHECK-NOT:                     InvokeStaticOrDirect
-
-  private static int signLong(long x) {
-    return Long.signum(x);
-  }
-
-
-  public static void testSignBoolean() {
-    expectEquals(0, signBoolean(false));
-    expectEquals(1, signBoolean(true));
-  }
-
-  public static void testSignByte() {
-    expectEquals(-1, signByte((byte)Byte.MIN_VALUE));
-    expectEquals(-1, signByte((byte)-64));
-    expectEquals(-1, signByte((byte)-1));
-    expectEquals(0, signByte((byte)0));
-    expectEquals(1, signByte((byte)1));
-    expectEquals(1, signByte((byte)64));
-    expectEquals(1, signByte((byte)Byte.MAX_VALUE));
-  }
-
-  public static void testSignShort() {
-    expectEquals(-1, signShort((short)Short.MIN_VALUE));
-    expectEquals(-1, signShort((short)-12345));
-    expectEquals(-1, signShort((short)-1));
-    expectEquals(0, signShort((short)0));
-    expectEquals(1, signShort((short)1));
-    expectEquals(1, signShort((short)12345));
-    expectEquals(1, signShort((short)Short.MAX_VALUE));
-  }
-
-  public static void testSignChar() {
-    expectEquals(0, signChar((char)0));
-    expectEquals(1, signChar((char)1));
-    expectEquals(1, signChar((char)12345));
-    expectEquals(1, signChar((char)Character.MAX_VALUE));
-  }
-
-  public static void testSignInt() {
-    expectEquals(-1, signInt(Integer.MIN_VALUE));
-    expectEquals(-1, signInt(-12345));
-    expectEquals(-1, signInt(-1));
-    expectEquals(0, signInt(0));
-    expectEquals(1, signInt(1));
-    expectEquals(1, signInt(12345));
-    expectEquals(1, signInt(Integer.MAX_VALUE));
-
-    for (int i = -11; i <= 11; i++) {
-      int expected = 0;
-      if (i < 0) expected = -1;
-      else if (i > 0) expected = 1;
-      expectEquals(expected, signInt(i));
-    }
-  }
-
-  public static void testSignLong() {
-    expectEquals(-1, signLong(Long.MIN_VALUE));
-    expectEquals(-1, signLong(-12345L));
-    expectEquals(-1, signLong(-1L));
-    expectEquals(0, signLong(0L));
-    expectEquals(1, signLong(1L));
-    expectEquals(1, signLong(12345L));
-    expectEquals(1, signLong(Long.MAX_VALUE));
-
-    expectEquals(-1, signLong(0x800000007FFFFFFFL));
-    expectEquals(-1, signLong(0x80000000FFFFFFFFL));
-    expectEquals(1, signLong(0x000000007FFFFFFFL));
-    expectEquals(1, signLong(0x00000000FFFFFFFFL));
-    expectEquals(1, signLong(0x7FFFFFFF7FFFFFFFL));
-    expectEquals(1, signLong(0x7FFFFFFFFFFFFFFFL));
-
-    for (long i = -11L; i <= 11L; i++) {
-      int expected = 0;
-      if (i < 0) expected = -1;
-      else if (i > 0) expected = 1;
-      expectEquals(expected, signLong(i));
-    }
-
-    for (long i = Long.MIN_VALUE; i <= Long.MIN_VALUE + 11L; i++) {
-      expectEquals(-1, signLong(i));
-    }
-
-    for (long i = Long.MAX_VALUE; i >= Long.MAX_VALUE - 11L; i--) {
-      expectEquals(1, signLong(i));
-    }
-  }
-
-
-  public static void main(String args[]) {
-    testSignBoolean();
-    testSignByte();
-    testSignShort();
-    testSignChar();
-    testSignInt();
-    testSignLong();
-
+  public static void main(String[] args) {
     System.out.println("passed");
   }
-
-  private static void expectEquals(int expected, int result) {
-    if (expected != result) {
-      throw new Error("Expected: " + expected + ", found: " + result);
-    }
-  }
 }
diff --git a/test/570-checker-osr/build b/test/570-checker-osr/build
deleted file mode 100644
index 10ffcc5..0000000
--- a/test/570-checker-osr/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/618-checker-induction/build b/test/618-checker-induction/build
deleted file mode 100644
index 10ffcc5..0000000
--- a/test/618-checker-induction/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index 0080ffa..1460725 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -290,7 +290,7 @@
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop1>>      outer_loop:none
   /// CHECK-DAG: <<Phi3:i\d+>> Phi               loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
   /// CHECK-DAG: <<Phi4:i\d+>> Phi               loop:<<Loop2>>      outer_loop:<<Loop1>>
-  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
   //
   /// CHECK-START: int Main.closedFormNested() loop_optimization (after)
   /// CHECK-NOT:               Phi
@@ -313,7 +313,7 @@
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop1>>      outer_loop:none
   /// CHECK-DAG: <<Phi3:i\d+>> Phi               loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
   /// CHECK-DAG: <<Phi4:i\d+>> Phi               loop:<<Loop2>>      outer_loop:<<Loop1>>
-  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
   //
   /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (after)
   /// CHECK-NOT:               Phi
@@ -411,7 +411,7 @@
   /// CHECK-START: int Main.periodicReturned9() loop_optimization (before)
   /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
   //
   /// CHECK-START: int Main.periodicReturned9() loop_optimization (after)
   /// CHECK-NOT:               Phi
@@ -430,7 +430,7 @@
   /// CHECK-START: int Main.periodicReturned10() loop_optimization (before)
   /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
   //
   /// CHECK-START: int Main.periodicReturned10() loop_optimization (after)
   /// CHECK-NOT:               Phi
@@ -450,7 +450,7 @@
   /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Phi3:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               Return [<<Phi3>>] loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
   //
   /// CHECK-START: int Main.getSum21() loop_optimization (after)
   /// CHECK-NOT:               Phi
@@ -505,7 +505,7 @@
   /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before)
   /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
   //
   /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (after)
   /// CHECK-NOT:               Phi
@@ -547,7 +547,7 @@
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop1>>      outer_loop:none
   /// CHECK-DAG: <<Phi3:i\d+>> Phi               loop:<<Loop2:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi4:i\d+>> Phi               loop:<<Loop2>>      outer_loop:none
-  /// CHECK-DAG:               Return [<<Phi3>>] loop:none
+  /// CHECK-DAG:               Return [<<Phi4>>] loop:none
   /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
   //
   /// CHECK-START: int Main.closedFeed() loop_optimization (after)
@@ -691,7 +691,7 @@
   /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (before)
   /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
   //
   /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (after)
   /// CHECK-NOT:               Phi
@@ -705,7 +705,7 @@
   /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (before)
   /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
   //
   /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (after)
   /// CHECK-NOT:               Phi
@@ -719,7 +719,7 @@
   /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (before)
   /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
   //
   /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (after)
   /// CHECK-NOT:               Phi
diff --git a/test/626-checker-arm64-scratch-register/build b/test/626-checker-arm64-scratch-register/build
deleted file mode 100644
index d85147f..0000000
--- a/test/626-checker-arm64-scratch-register/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# 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
-#
-# 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.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/626-checker-arm64-scratch-register/smali/Main2.smali b/test/626-checker-arm64-scratch-register/smali/Main2.smali
new file mode 100644
index 0000000..914ae6e
--- /dev/null
+++ b/test/626-checker-arm64-scratch-register/smali/Main2.smali
@@ -0,0 +1,1768 @@
+# 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.
+
+.class public LMain2;
+.super Ljava/lang/Object;
+
+
+# instance fields
+.field b00:Z
+
+.field b01:Z
+
+.field b02:Z
+
+.field b03:Z
+
+.field b04:Z
+
+.field b05:Z
+
+.field b06:Z
+
+.field b07:Z
+
+.field b08:Z
+
+.field b09:Z
+
+.field b10:Z
+
+.field b11:Z
+
+.field b12:Z
+
+.field b13:Z
+
+.field b14:Z
+
+.field b15:Z
+
+.field b16:Z
+
+.field b17:Z
+
+.field b18:Z
+
+.field b19:Z
+
+.field b20:Z
+
+.field b21:Z
+
+.field b22:Z
+
+.field b23:Z
+
+.field b24:Z
+
+.field b25:Z
+
+.field b26:Z
+
+.field b27:Z
+
+.field b28:Z
+
+.field b29:Z
+
+.field b30:Z
+
+.field b31:Z
+
+.field b32:Z
+
+.field b33:Z
+
+.field b34:Z
+
+.field b35:Z
+
+.field b36:Z
+
+.field conditionA:Z
+
+.field conditionB:Z
+
+.field conditionC:Z
+
+
+# direct methods
+.method public constructor <init>()V
+    .registers 1
+
+    .prologue
+    .line 17
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+    return-void
+.end method
+
+## CHECK-START-ARM64: void Main2.test() register (after)
+## CHECK: begin_block
+## CHECK:   name "B0"
+## CHECK:       <<This:l\d+>>  ParameterValue
+## CHECK: end_block
+## CHECK: begin_block
+## CHECK:   successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
+## CHECK:       <<CondB:z\d+>>  InstanceFieldGet [<<This>>] field_name:Main2.conditionB
+## CHECK:                       If [<<CondB>>]
+## CHECK:  end_block
+## CHECK: begin_block
+## CHECK:   name "<<ElseBlock>>"
+## CHECK:                      ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)]
+## CHECK: end_block
+
+## CHECK-START-ARM64: void Main2.test() disassembly (after)
+## CHECK: begin_block
+## CHECK:   name "B0"
+## CHECK:       <<This:l\d+>>  ParameterValue
+## CHECK: end_block
+## CHECK: begin_block
+## CHECK:   successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
+## CHECK:       <<CondB:z\d+>>  InstanceFieldGet [<<This>>] field_name:Main2.conditionB
+## CHECK:                       If [<<CondB>>]
+## CHECK:  end_block
+## CHECK: begin_block
+## CHECK:   name "<<ElseBlock>>"
+## CHECK:                      ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid]
+## CHECK:                        fmov d31, d2
+## CHECK:                        ldr s2, [sp, #36]
+## CHECK:                        ldr w16, [sp, #16]
+## CHECK:                        str w16, [sp, #36]
+## CHECK:                        str s14, [sp, #16]
+## CHECK:                        ldr s14, [sp, #28]
+## CHECK:                        str s1, [sp, #28]
+## CHECK:                        ldr s1, [sp, #32]
+## CHECK:                        str s31, [sp, #32]
+## CHECK:                        ldr s31, [sp, #20]
+## CHECK:                        str s31, [sp, #40]
+## CHECK:                        str s12, [sp, #20]
+## CHECK:                        fmov d12, d11
+## CHECK:                        fmov d11, d10
+## CHECK:                        fmov d10, d23
+## CHECK:                        fmov d23, d22
+## CHECK:                        fmov d22, d21
+## CHECK:                        fmov d21, d20
+## CHECK:                        fmov d20, d19
+## CHECK:                        fmov d19, d18
+## CHECK:                        fmov d18, d7
+## CHECK:                        fmov d7, d6
+## CHECK:                        fmov d6, d5
+## CHECK:                        fmov d5, d4
+## CHECK:                        fmov d4, d3
+## CHECK:                        fmov d3, d13
+## CHECK:                        ldr s13, [sp, #24]
+## CHECK:                        str s3, [sp, #24]
+## CHECK:                        ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100)
+## CHECK: end_block
+
+# Original java source:
+#
+#     public void test() {
+#       String r = "";
+#
+#       // For the purpose of this regression test, the order of
+#       // definition of these float variable matters.  Likewise with the
+#       // order of the instructions where these variables are used below.
+#       // Reordering these lines make make the original (b/32545705)
+#       // issue vanish.
+#       float f17 = b17 ? 0.0f : 1.0f;
+#       float f16 = b16 ? 0.0f : 1.0f;
+#       float f18 = b18 ? 0.0f : 1.0f;
+#       float f19 = b19 ? 0.0f : 1.0f;
+#       float f20 = b20 ? 0.0f : 1.0f;
+#       float f21 = b21 ? 0.0f : 1.0f;
+#       float f15 = b15 ? 0.0f : 1.0f;
+#       float f00 = b00 ? 0.0f : 1.0f;
+#       float f22 = b22 ? 0.0f : 1.0f;
+#       float f23 = b23 ? 0.0f : 1.0f;
+#       float f24 = b24 ? 0.0f : 1.0f;
+#       float f25 = b25 ? 0.0f : 1.0f;
+#       float f26 = b26 ? 0.0f : 1.0f;
+#       float f27 = b27 ? 0.0f : 1.0f;
+#       float f29 = b29 ? 0.0f : 1.0f;
+#       float f28 = b28 ? 0.0f : 1.0f;
+#       float f01 = b01 ? 0.0f : 1.0f;
+#       float f02 = b02 ? 0.0f : 1.0f;
+#       float f03 = b03 ? 0.0f : 1.0f;
+#       float f04 = b04 ? 0.0f : 1.0f;
+#       float f05 = b05 ? 0.0f : 1.0f;
+#       float f07 = b07 ? 0.0f : 1.0f;
+#       float f06 = b06 ? 0.0f : 1.0f;
+#       float f30 = b30 ? 0.0f : 1.0f;
+#       float f31 = b31 ? 0.0f : 1.0f;
+#       float f32 = b32 ? 0.0f : 1.0f;
+#       float f33 = b33 ? 0.0f : 1.0f;
+#       float f34 = b34 ? 0.0f : 1.0f;
+#       float f36 = b36 ? 0.0f : 1.0f;
+#       float f35 = b35 ? 0.0f : 1.0f;
+#       float f08 = b08 ? 0.0f : 1.0f;
+#       float f09 = b09 ? 0.0f : 1.0f;
+#       float f10 = b10 ? 0.0f : 1.0f;
+#       float f11 = b11 ? 0.0f : 1.0f;
+#       float f12 = b12 ? 0.0f : 1.0f;
+#       float f14 = b14 ? 0.0f : 1.0f;
+#       float f13 = b13 ? 0.0f : 1.0f;
+#
+#       if (conditionA) {
+#         f16 /= 1000.0f;
+#         f17 /= 1000.0f;
+#         f18 /= 1000.0f;
+#         f19 /= 1000.0f;
+#         f20 /= 1000.0f;
+#         f21 /= 1000.0f;
+#         f15 /= 1000.0f;
+#         f08 /= 1000.0f;
+#         f09 /= 1000.0f;
+#         f10 /= 1000.0f;
+#         f11 /= 1000.0f;
+#         f12 /= 1000.0f;
+#         f30 /= 1000.0f;
+#         f31 /= 1000.0f;
+#         f32 /= 1000.0f;
+#         f33 /= 1000.0f;
+#         f34 /= 1000.0f;
+#         f01 /= 1000.0f;
+#         f02 /= 1000.0f;
+#         f03 /= 1000.0f;
+#         f04 /= 1000.0f;
+#         f05 /= 1000.0f;
+#         f23 /= 1000.0f;
+#         f24 /= 1000.0f;
+#         f25 /= 1000.0f;
+#         f26 /= 1000.0f;
+#         f27 /= 1000.0f;
+#         f22 /= 1000.0f;
+#         f00 /= 1000.0f;
+#         f14 /= 1000.0f;
+#         f13 /= 1000.0f;
+#         f36 /= 1000.0f;
+#         f35 /= 1000.0f;
+#         f07 /= 1000.0f;
+#         f06 /= 1000.0f;
+#         f29 /= 1000.0f;
+#         f28 /= 1000.0f;
+#       }
+#       // The parallel move that used to exhaust the ARM64 parallel move
+#       // resolver's scratch register pool (provided by VIXL) was in the
+#       // "else" branch of the following condition generated by ART's
+#       // compiler.
+#       if (conditionB) {
+#         f16 /= 100.0f;
+#         f17 /= 100.0f;
+#         f18 /= 100.0f;
+#         f19 /= 100.0f;
+#         f20 /= 100.0f;
+#         f21 /= 100.0f;
+#         f15 /= 100.0f;
+#         f08 /= 100.0f;
+#         f09 /= 100.0f;
+#         f10 /= 100.0f;
+#         f11 /= 100.0f;
+#         f12 /= 100.0f;
+#         f30 /= 100.0f;
+#         f31 /= 100.0f;
+#         f32 /= 100.0f;
+#         f33 /= 100.0f;
+#         f34 /= 100.0f;
+#         f01 /= 100.0f;
+#         f02 /= 100.0f;
+#         f03 /= 100.0f;
+#         f04 /= 100.0f;
+#         f05 /= 100.0f;
+#         f23 /= 100.0f;
+#         f24 /= 100.0f;
+#         f25 /= 100.0f;
+#         f26 /= 100.0f;
+#         f27 /= 100.0f;
+#         f22 /= 100.0f;
+#         f00 /= 100.0f;
+#         f14 /= 100.0f;
+#         f13 /= 100.0f;
+#         f36 /= 100.0f;
+#         f35 /= 100.0f;
+#         f07 /= 100.0f;
+#         f06 /= 100.0f;
+#         f29 /= 100.0f;
+#         f28 /= 100.0f;
+#       }
+#       if (conditionC) {
+#         f16 /= 12.0f;
+#         f17 /= 12.0f;
+#         f18 /= 12.0f;
+#         f19 /= 12.0f;
+#         f20 /= 12.0f;
+#         f21 /= 12.0f;
+#         f15 /= 12.0f;
+#         f08 /= 12.0f;
+#         f09 /= 12.0f;
+#         f10 /= 12.0f;
+#         f11 /= 12.0f;
+#         f12 /= 12.0f;
+#         f30 /= 12.0f;
+#         f31 /= 12.0f;
+#         f32 /= 12.0f;
+#         f33 /= 12.0f;
+#         f34 /= 12.0f;
+#         f01 /= 12.0f;
+#         f02 /= 12.0f;
+#         f03 /= 12.0f;
+#         f04 /= 12.0f;
+#         f05 /= 12.0f;
+#         f23 /= 12.0f;
+#         f24 /= 12.0f;
+#         f25 /= 12.0f;
+#         f26 /= 12.0f;
+#         f27 /= 12.0f;
+#         f22 /= 12.0f;
+#         f00 /= 12.0f;
+#         f14 /= 12.0f;
+#         f13 /= 12.0f;
+#         f36 /= 12.0f;
+#         f35 /= 12.0f;
+#         f07 /= 12.0f;
+#         f06 /= 12.0f;
+#         f29 /= 12.0f;
+#         f28 /= 12.0f;
+#       }
+#       float s = 0.0f;
+#       s = ((float) Math.round(100.0f * s)) / 100.0f;
+#       String res = s + r;
+#     }
+
+# virtual methods
+.method public test()V
+    .registers 45
+
+    .prologue
+    .line 121
+    const-string v39, ""
+
+    .line 128
+    .local v39, "r":Ljava/lang/String;
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b17:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_367
+
+    const/16 v19, 0x0
+
+    .line 129
+    .local v19, "f17":F
+    :goto_c
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b16:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_36b
+
+    const/16 v18, 0x0
+
+    .line 130
+    .local v18, "f16":F
+    :goto_16
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b18:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_36f
+
+    const/16 v20, 0x0
+
+    .line 131
+    .local v20, "f18":F
+    :goto_20
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b19:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_373
+
+    const/16 v21, 0x0
+
+    .line 132
+    .local v21, "f19":F
+    :goto_2a
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b20:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_377
+
+    const/16 v22, 0x0
+
+    .line 133
+    .local v22, "f20":F
+    :goto_34
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b21:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_37b
+
+    const/16 v23, 0x0
+
+    .line 134
+    .local v23, "f21":F
+    :goto_3e
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b15:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_37f
+
+    const/16 v17, 0x0
+
+    .line 135
+    .local v17, "f15":F
+    :goto_48
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b00:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_383
+
+    const/4 v2, 0x0
+
+    .line 136
+    .local v2, "f00":F
+    :goto_51
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b22:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_387
+
+    const/16 v24, 0x0
+
+    .line 137
+    .local v24, "f22":F
+    :goto_5b
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b23:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_38b
+
+    const/16 v25, 0x0
+
+    .line 138
+    .local v25, "f23":F
+    :goto_65
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b24:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_38f
+
+    const/16 v26, 0x0
+
+    .line 139
+    .local v26, "f24":F
+    :goto_6f
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b25:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_393
+
+    const/16 v27, 0x0
+
+    .line 140
+    .local v27, "f25":F
+    :goto_79
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b26:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_397
+
+    const/16 v28, 0x0
+
+    .line 141
+    .local v28, "f26":F
+    :goto_83
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b27:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_39b
+
+    const/16 v29, 0x0
+
+    .line 142
+    .local v29, "f27":F
+    :goto_8d
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b29:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_39f
+
+    const/16 v31, 0x0
+
+    .line 143
+    .local v31, "f29":F
+    :goto_97
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b28:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3a3
+
+    const/16 v30, 0x0
+
+    .line 144
+    .local v30, "f28":F
+    :goto_a1
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b01:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3a7
+
+    const/4 v3, 0x0
+
+    .line 145
+    .local v3, "f01":F
+    :goto_aa
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b02:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3ab
+
+    const/4 v4, 0x0
+
+    .line 146
+    .local v4, "f02":F
+    :goto_b3
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b03:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3af
+
+    const/4 v5, 0x0
+
+    .line 147
+    .local v5, "f03":F
+    :goto_bc
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b04:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3b3
+
+    const/4 v6, 0x0
+
+    .line 148
+    .local v6, "f04":F
+    :goto_c5
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b05:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3b7
+
+    const/4 v7, 0x0
+
+    .line 149
+    .local v7, "f05":F
+    :goto_ce
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b07:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3bb
+
+    const/4 v9, 0x0
+
+    .line 150
+    .local v9, "f07":F
+    :goto_d7
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b06:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3bf
+
+    const/4 v8, 0x0
+
+    .line 151
+    .local v8, "f06":F
+    :goto_e0
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b30:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3c3
+
+    const/16 v32, 0x0
+
+    .line 152
+    .local v32, "f30":F
+    :goto_ea
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b31:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3c7
+
+    const/16 v33, 0x0
+
+    .line 153
+    .local v33, "f31":F
+    :goto_f4
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b32:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3cb
+
+    const/16 v34, 0x0
+
+    .line 154
+    .local v34, "f32":F
+    :goto_fe
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b33:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3cf
+
+    const/16 v35, 0x0
+
+    .line 155
+    .local v35, "f33":F
+    :goto_108
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b34:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3d3
+
+    const/16 v36, 0x0
+
+    .line 156
+    .local v36, "f34":F
+    :goto_112
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b36:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3d7
+
+    const/16 v38, 0x0
+
+    .line 157
+    .local v38, "f36":F
+    :goto_11c
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b35:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3db
+
+    const/16 v37, 0x0
+
+    .line 158
+    .local v37, "f35":F
+    :goto_126
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b08:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3df
+
+    const/4 v10, 0x0
+
+    .line 159
+    .local v10, "f08":F
+    :goto_12f
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b09:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3e3
+
+    const/4 v11, 0x0
+
+    .line 160
+    .local v11, "f09":F
+    :goto_138
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b10:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3e7
+
+    const/4 v12, 0x0
+
+    .line 161
+    .local v12, "f10":F
+    :goto_141
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b11:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3eb
+
+    const/4 v13, 0x0
+
+    .line 162
+    .local v13, "f11":F
+    :goto_14a
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b12:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3ef
+
+    const/4 v14, 0x0
+
+    .line 163
+    .local v14, "f12":F
+    :goto_153
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b14:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3f3
+
+    const/16 v16, 0x0
+
+    .line 164
+    .local v16, "f14":F
+    :goto_15d
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->b13:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_3f7
+
+    const/4 v15, 0x0
+
+    .line 166
+    .local v15, "f13":F
+    :goto_166
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->conditionA:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_202
+
+    .line 167
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v18, v18, v42
+
+    .line 168
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v19, v19, v42
+
+    .line 169
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v20, v20, v42
+
+    .line 170
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v21, v21, v42
+
+    .line 171
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v22, v22, v42
+
+    .line 172
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v23, v23, v42
+
+    .line 173
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v17, v17, v42
+
+    .line 174
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v10, v10, v42
+
+    .line 175
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v11, v11, v42
+
+    .line 176
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v12, v12, v42
+
+    .line 177
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v13, v13, v42
+
+    .line 178
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v14, v14, v42
+
+    .line 179
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v32, v32, v42
+
+    .line 180
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v33, v33, v42
+
+    .line 181
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v34, v34, v42
+
+    .line 182
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v35, v35, v42
+
+    .line 183
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v36, v36, v42
+
+    .line 184
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v3, v3, v42
+
+    .line 185
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v4, v4, v42
+
+    .line 186
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v5, v5, v42
+
+    .line 187
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v6, v6, v42
+
+    .line 188
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v7, v7, v42
+
+    .line 189
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v25, v25, v42
+
+    .line 190
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v26, v26, v42
+
+    .line 191
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v27, v27, v42
+
+    .line 192
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v28, v28, v42
+
+    .line 193
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v29, v29, v42
+
+    .line 194
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v24, v24, v42
+
+    .line 195
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v2, v2, v42
+
+    .line 196
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v16, v16, v42
+
+    .line 197
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v15, v15, v42
+
+    .line 198
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v38, v38, v42
+
+    .line 199
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v37, v37, v42
+
+    .line 200
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v9, v9, v42
+
+    .line 201
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v8, v8, v42
+
+    .line 202
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v31, v31, v42
+
+    .line 203
+    const/high16 v42, 0x447a0000    # 1000.0f
+
+    div-float v30, v30, v42
+
+    .line 209
+    :cond_202
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->conditionB:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_29e
+
+    .line 210
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v18, v18, v42
+
+    .line 211
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v19, v19, v42
+
+    .line 212
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v20, v20, v42
+
+    .line 213
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v21, v21, v42
+
+    .line 214
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v22, v22, v42
+
+    .line 215
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v23, v23, v42
+
+    .line 216
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v17, v17, v42
+
+    .line 217
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v10, v10, v42
+
+    .line 218
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v11, v11, v42
+
+    .line 219
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v12, v12, v42
+
+    .line 220
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v13, v13, v42
+
+    .line 221
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v14, v14, v42
+
+    .line 222
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v32, v32, v42
+
+    .line 223
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v33, v33, v42
+
+    .line 224
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v34, v34, v42
+
+    .line 225
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v35, v35, v42
+
+    .line 226
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v36, v36, v42
+
+    .line 227
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v3, v3, v42
+
+    .line 228
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v4, v4, v42
+
+    .line 229
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v5, v5, v42
+
+    .line 230
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v6, v6, v42
+
+    .line 231
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v7, v7, v42
+
+    .line 232
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v25, v25, v42
+
+    .line 233
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v26, v26, v42
+
+    .line 234
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v27, v27, v42
+
+    .line 235
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v28, v28, v42
+
+    .line 236
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v29, v29, v42
+
+    .line 237
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v24, v24, v42
+
+    .line 238
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v2, v2, v42
+
+    .line 239
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v16, v16, v42
+
+    .line 240
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v15, v15, v42
+
+    .line 241
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v38, v38, v42
+
+    .line 242
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v37, v37, v42
+
+    .line 243
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v9, v9, v42
+
+    .line 244
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v8, v8, v42
+
+    .line 245
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v31, v31, v42
+
+    .line 246
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    div-float v30, v30, v42
+
+    .line 248
+    :cond_29e
+    move-object/from16 v0, p0
+
+    iget-boolean v0, v0, LMain2;->conditionC:Z
+
+    move/from16 v42, v0
+
+    if-eqz v42, :cond_33a
+
+    .line 249
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v18, v18, v42
+
+    .line 250
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v19, v19, v42
+
+    .line 251
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v20, v20, v42
+
+    .line 252
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v21, v21, v42
+
+    .line 253
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v22, v22, v42
+
+    .line 254
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v23, v23, v42
+
+    .line 255
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v17, v17, v42
+
+    .line 256
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v10, v10, v42
+
+    .line 257
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v11, v11, v42
+
+    .line 258
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v12, v12, v42
+
+    .line 259
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v13, v13, v42
+
+    .line 260
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v14, v14, v42
+
+    .line 261
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v32, v32, v42
+
+    .line 262
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v33, v33, v42
+
+    .line 263
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v34, v34, v42
+
+    .line 264
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v35, v35, v42
+
+    .line 265
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v36, v36, v42
+
+    .line 266
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v3, v3, v42
+
+    .line 267
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v4, v4, v42
+
+    .line 268
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v5, v5, v42
+
+    .line 269
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v6, v6, v42
+
+    .line 270
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v7, v7, v42
+
+    .line 271
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v25, v25, v42
+
+    .line 272
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v26, v26, v42
+
+    .line 273
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v27, v27, v42
+
+    .line 274
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v28, v28, v42
+
+    .line 275
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v29, v29, v42
+
+    .line 276
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v24, v24, v42
+
+    .line 277
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v2, v2, v42
+
+    .line 278
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v16, v16, v42
+
+    .line 279
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v15, v15, v42
+
+    .line 280
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v38, v38, v42
+
+    .line 281
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v37, v37, v42
+
+    .line 282
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v9, v9, v42
+
+    .line 283
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v8, v8, v42
+
+    .line 284
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v31, v31, v42
+
+    .line 285
+    const/high16 v42, 0x41400000    # 12.0f
+
+    div-float v30, v30, v42
+
+    .line 287
+    :cond_33a
+    const/16 v41, 0x0
+
+    .line 288
+    .local v41, "s":F
+    const/high16 v42, 0x42c80000    # 100.0f
+
+    mul-float v42, v42, v41
+
+    invoke-static/range {v42 .. v42}, Ljava/lang/Math;->round(F)I
+
+    move-result v42
+
+    move/from16 v0, v42
+
+    int-to-float v0, v0
+
+    move/from16 v42, v0
+
+    const/high16 v43, 0x42c80000    # 100.0f
+
+    div-float v41, v42, v43
+
+    .line 289
+    new-instance v42, Ljava/lang/StringBuilder;
+
+    invoke-direct/range {v42 .. v42}, Ljava/lang/StringBuilder;-><init>()V
+
+    move-object/from16 v0, v42
+
+    move/from16 v1, v41
+
+    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder;
+
+    move-result-object v42
+
+    move-object/from16 v0, v42
+
+    move-object/from16 v1, v39
+
+    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v42
+
+    invoke-virtual/range {v42 .. v42}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+
+    move-result-object v40
+
+    .line 290
+    .local v40, "res":Ljava/lang/String;
+    return-void
+
+    .line 128
+    .end local v2    # "f00":F
+    .end local v3    # "f01":F
+    .end local v4    # "f02":F
+    .end local v5    # "f03":F
+    .end local v6    # "f04":F
+    .end local v7    # "f05":F
+    .end local v8    # "f06":F
+    .end local v9    # "f07":F
+    .end local v10    # "f08":F
+    .end local v11    # "f09":F
+    .end local v12    # "f10":F
+    .end local v13    # "f11":F
+    .end local v14    # "f12":F
+    .end local v15    # "f13":F
+    .end local v16    # "f14":F
+    .end local v17    # "f15":F
+    .end local v18    # "f16":F
+    .end local v19    # "f17":F
+    .end local v20    # "f18":F
+    .end local v21    # "f19":F
+    .end local v22    # "f20":F
+    .end local v23    # "f21":F
+    .end local v24    # "f22":F
+    .end local v25    # "f23":F
+    .end local v26    # "f24":F
+    .end local v27    # "f25":F
+    .end local v28    # "f26":F
+    .end local v29    # "f27":F
+    .end local v30    # "f28":F
+    .end local v31    # "f29":F
+    .end local v32    # "f30":F
+    .end local v33    # "f31":F
+    .end local v34    # "f32":F
+    .end local v35    # "f33":F
+    .end local v36    # "f34":F
+    .end local v37    # "f35":F
+    .end local v38    # "f36":F
+    .end local v40    # "res":Ljava/lang/String;
+    .end local v41    # "s":F
+    :cond_367
+    const/high16 v19, 0x3f800000    # 1.0f
+
+    goto/16 :goto_c
+
+    .line 129
+    .restart local v19    # "f17":F
+    :cond_36b
+    const/high16 v18, 0x3f800000    # 1.0f
+
+    goto/16 :goto_16
+
+    .line 130
+    .restart local v18    # "f16":F
+    :cond_36f
+    const/high16 v20, 0x3f800000    # 1.0f
+
+    goto/16 :goto_20
+
+    .line 131
+    .restart local v20    # "f18":F
+    :cond_373
+    const/high16 v21, 0x3f800000    # 1.0f
+
+    goto/16 :goto_2a
+
+    .line 132
+    .restart local v21    # "f19":F
+    :cond_377
+    const/high16 v22, 0x3f800000    # 1.0f
+
+    goto/16 :goto_34
+
+    .line 133
+    .restart local v22    # "f20":F
+    :cond_37b
+    const/high16 v23, 0x3f800000    # 1.0f
+
+    goto/16 :goto_3e
+
+    .line 134
+    .restart local v23    # "f21":F
+    :cond_37f
+    const/high16 v17, 0x3f800000    # 1.0f
+
+    goto/16 :goto_48
+
+    .line 135
+    .restart local v17    # "f15":F
+    :cond_383
+    const/high16 v2, 0x3f800000    # 1.0f
+
+    goto/16 :goto_51
+
+    .line 136
+    .restart local v2    # "f00":F
+    :cond_387
+    const/high16 v24, 0x3f800000    # 1.0f
+
+    goto/16 :goto_5b
+
+    .line 137
+    .restart local v24    # "f22":F
+    :cond_38b
+    const/high16 v25, 0x3f800000    # 1.0f
+
+    goto/16 :goto_65
+
+    .line 138
+    .restart local v25    # "f23":F
+    :cond_38f
+    const/high16 v26, 0x3f800000    # 1.0f
+
+    goto/16 :goto_6f
+
+    .line 139
+    .restart local v26    # "f24":F
+    :cond_393
+    const/high16 v27, 0x3f800000    # 1.0f
+
+    goto/16 :goto_79
+
+    .line 140
+    .restart local v27    # "f25":F
+    :cond_397
+    const/high16 v28, 0x3f800000    # 1.0f
+
+    goto/16 :goto_83
+
+    .line 141
+    .restart local v28    # "f26":F
+    :cond_39b
+    const/high16 v29, 0x3f800000    # 1.0f
+
+    goto/16 :goto_8d
+
+    .line 142
+    .restart local v29    # "f27":F
+    :cond_39f
+    const/high16 v31, 0x3f800000    # 1.0f
+
+    goto/16 :goto_97
+
+    .line 143
+    .restart local v31    # "f29":F
+    :cond_3a3
+    const/high16 v30, 0x3f800000    # 1.0f
+
+    goto/16 :goto_a1
+
+    .line 144
+    .restart local v30    # "f28":F
+    :cond_3a7
+    const/high16 v3, 0x3f800000    # 1.0f
+
+    goto/16 :goto_aa
+
+    .line 145
+    .restart local v3    # "f01":F
+    :cond_3ab
+    const/high16 v4, 0x3f800000    # 1.0f
+
+    goto/16 :goto_b3
+
+    .line 146
+    .restart local v4    # "f02":F
+    :cond_3af
+    const/high16 v5, 0x3f800000    # 1.0f
+
+    goto/16 :goto_bc
+
+    .line 147
+    .restart local v5    # "f03":F
+    :cond_3b3
+    const/high16 v6, 0x3f800000    # 1.0f
+
+    goto/16 :goto_c5
+
+    .line 148
+    .restart local v6    # "f04":F
+    :cond_3b7
+    const/high16 v7, 0x3f800000    # 1.0f
+
+    goto/16 :goto_ce
+
+    .line 149
+    .restart local v7    # "f05":F
+    :cond_3bb
+    const/high16 v9, 0x3f800000    # 1.0f
+
+    goto/16 :goto_d7
+
+    .line 150
+    .restart local v9    # "f07":F
+    :cond_3bf
+    const/high16 v8, 0x3f800000    # 1.0f
+
+    goto/16 :goto_e0
+
+    .line 151
+    .restart local v8    # "f06":F
+    :cond_3c3
+    const/high16 v32, 0x3f800000    # 1.0f
+
+    goto/16 :goto_ea
+
+    .line 152
+    .restart local v32    # "f30":F
+    :cond_3c7
+    const/high16 v33, 0x3f800000    # 1.0f
+
+    goto/16 :goto_f4
+
+    .line 153
+    .restart local v33    # "f31":F
+    :cond_3cb
+    const/high16 v34, 0x3f800000    # 1.0f
+
+    goto/16 :goto_fe
+
+    .line 154
+    .restart local v34    # "f32":F
+    :cond_3cf
+    const/high16 v35, 0x3f800000    # 1.0f
+
+    goto/16 :goto_108
+
+    .line 155
+    .restart local v35    # "f33":F
+    :cond_3d3
+    const/high16 v36, 0x3f800000    # 1.0f
+
+    goto/16 :goto_112
+
+    .line 156
+    .restart local v36    # "f34":F
+    :cond_3d7
+    const/high16 v38, 0x3f800000    # 1.0f
+
+    goto/16 :goto_11c
+
+    .line 157
+    .restart local v38    # "f36":F
+    :cond_3db
+    const/high16 v37, 0x3f800000    # 1.0f
+
+    goto/16 :goto_126
+
+    .line 158
+    .restart local v37    # "f35":F
+    :cond_3df
+    const/high16 v10, 0x3f800000    # 1.0f
+
+    goto/16 :goto_12f
+
+    .line 159
+    .restart local v10    # "f08":F
+    :cond_3e3
+    const/high16 v11, 0x3f800000    # 1.0f
+
+    goto/16 :goto_138
+
+    .line 160
+    .restart local v11    # "f09":F
+    :cond_3e7
+    const/high16 v12, 0x3f800000    # 1.0f
+
+    goto/16 :goto_141
+
+    .line 161
+    .restart local v12    # "f10":F
+    :cond_3eb
+    const/high16 v13, 0x3f800000    # 1.0f
+
+    goto/16 :goto_14a
+
+    .line 162
+    .restart local v13    # "f11":F
+    :cond_3ef
+    const/high16 v14, 0x3f800000    # 1.0f
+
+    goto/16 :goto_153
+
+    .line 163
+    .restart local v14    # "f12":F
+    :cond_3f3
+    const/high16 v16, 0x3f800000    # 1.0f
+
+    goto/16 :goto_15d
+
+    .line 164
+    .restart local v16    # "f14":F
+    :cond_3f7
+    const/high16 v15, 0x3f800000    # 1.0f
+
+    goto/16 :goto_166
+.end method
diff --git a/test/626-checker-arm64-scratch-register/src-art/Main.java b/test/626-checker-arm64-scratch-register/src-art/Main.java
new file mode 100644
index 0000000..b816586
--- /dev/null
+++ b/test/626-checker-arm64-scratch-register/src-art/Main.java
@@ -0,0 +1,23 @@
+/*
+ * 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) throws Exception {
+    Class main2 = Class.forName("Main2");
+    main2.getMethod("test").invoke(main2.newInstance());
+    System.out.println("passed");
+  }
+}
diff --git a/test/626-checker-arm64-scratch-register/src/Main.java b/test/626-checker-arm64-scratch-register/src/Main.java
index 1394917..fa8e5cd 100644
--- a/test/626-checker-arm64-scratch-register/src/Main.java
+++ b/test/626-checker-arm64-scratch-register/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,284 +14,9 @@
  * limitations under the License.
  */
 
+// This file is just for running on the RI as the test is ART specific.
 public class Main {
-
-  boolean b00;
-  boolean b01;
-  boolean b02;
-  boolean b03;
-  boolean b04;
-  boolean b05;
-  boolean b06;
-  boolean b07;
-  boolean b08;
-  boolean b09;
-  boolean b10;
-  boolean b11;
-  boolean b12;
-  boolean b13;
-  boolean b14;
-  boolean b15;
-  boolean b16;
-  boolean b17;
-  boolean b18;
-  boolean b19;
-  boolean b20;
-  boolean b21;
-  boolean b22;
-  boolean b23;
-  boolean b24;
-  boolean b25;
-  boolean b26;
-  boolean b27;
-  boolean b28;
-  boolean b29;
-  boolean b30;
-  boolean b31;
-  boolean b32;
-  boolean b33;
-  boolean b34;
-  boolean b35;
-  boolean b36;
-
-  boolean conditionA;
-  boolean conditionB;
-  boolean conditionC;
-
-  /// CHECK-START-ARM64: void Main.test() register (after)
-  /// CHECK: begin_block
-  /// CHECK:   name "B0"
-  /// CHECK:       <<This:l\d+>>  ParameterValue
-  /// CHECK: end_block
-  /// CHECK: begin_block
-  /// CHECK:   successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
-  /// CHECK:       <<CondB:z\d+>>  InstanceFieldGet [<<This>>] field_name:Main.conditionB
-  /// CHECK:                       If [<<CondB>>]
-  /// CHECK:  end_block
-  /// CHECK: begin_block
-  /// CHECK:   name "<<ElseBlock>>"
-  /// CHECK:                      ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)]
-  /// CHECK: end_block
-
-  /// CHECK-START-ARM64: void Main.test() disassembly (after)
-  /// CHECK: begin_block
-  /// CHECK:   name "B0"
-  /// CHECK:       <<This:l\d+>>  ParameterValue
-  /// CHECK: end_block
-  /// CHECK: begin_block
-  /// CHECK:   successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
-  /// CHECK:       <<CondB:z\d+>>  InstanceFieldGet [<<This>>] field_name:Main.conditionB
-  /// CHECK:                       If [<<CondB>>]
-  /// CHECK:  end_block
-  /// CHECK: begin_block
-  /// CHECK:   name "<<ElseBlock>>"
-  /// CHECK:                      ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid]
-  /// CHECK:                        fmov d31, d2
-  /// CHECK:                        ldr s2, [sp, #36]
-  /// CHECK:                        ldr w16, [sp, #16]
-  /// CHECK:                        str w16, [sp, #36]
-  /// CHECK:                        str s14, [sp, #16]
-  /// CHECK:                        ldr s14, [sp, #28]
-  /// CHECK:                        str s1, [sp, #28]
-  /// CHECK:                        ldr s1, [sp, #32]
-  /// CHECK:                        str s31, [sp, #32]
-  /// CHECK:                        ldr s31, [sp, #20]
-  /// CHECK:                        str s31, [sp, #40]
-  /// CHECK:                        str s12, [sp, #20]
-  /// CHECK:                        fmov d12, d11
-  /// CHECK:                        fmov d11, d10
-  /// CHECK:                        fmov d10, d23
-  /// CHECK:                        fmov d23, d22
-  /// CHECK:                        fmov d22, d21
-  /// CHECK:                        fmov d21, d20
-  /// CHECK:                        fmov d20, d19
-  /// CHECK:                        fmov d19, d18
-  /// CHECK:                        fmov d18, d7
-  /// CHECK:                        fmov d7, d6
-  /// CHECK:                        fmov d6, d5
-  /// CHECK:                        fmov d5, d4
-  /// CHECK:                        fmov d4, d3
-  /// CHECK:                        fmov d3, d13
-  /// CHECK:                        ldr s13, [sp, #24]
-  /// CHECK:                        str s3, [sp, #24]
-  /// CHECK:                        ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100)
-  /// CHECK: end_block
-
-  public void test() {
-    String r = "";
-
-    // For the purpose of this regression test, the order of
-    // definition of these float variable matters.  Likewise with the
-    // order of the instructions where these variables are used below.
-    // Reordering these lines make make the original (b/32545705)
-    // issue vanish.
-    float f17 = b17 ? 0.0f : 1.0f;
-    float f16 = b16 ? 0.0f : 1.0f;
-    float f18 = b18 ? 0.0f : 1.0f;
-    float f19 = b19 ? 0.0f : 1.0f;
-    float f20 = b20 ? 0.0f : 1.0f;
-    float f21 = b21 ? 0.0f : 1.0f;
-    float f15 = b15 ? 0.0f : 1.0f;
-    float f00 = b00 ? 0.0f : 1.0f;
-    float f22 = b22 ? 0.0f : 1.0f;
-    float f23 = b23 ? 0.0f : 1.0f;
-    float f24 = b24 ? 0.0f : 1.0f;
-    float f25 = b25 ? 0.0f : 1.0f;
-    float f26 = b26 ? 0.0f : 1.0f;
-    float f27 = b27 ? 0.0f : 1.0f;
-    float f29 = b29 ? 0.0f : 1.0f;
-    float f28 = b28 ? 0.0f : 1.0f;
-    float f01 = b01 ? 0.0f : 1.0f;
-    float f02 = b02 ? 0.0f : 1.0f;
-    float f03 = b03 ? 0.0f : 1.0f;
-    float f04 = b04 ? 0.0f : 1.0f;
-    float f05 = b05 ? 0.0f : 1.0f;
-    float f07 = b07 ? 0.0f : 1.0f;
-    float f06 = b06 ? 0.0f : 1.0f;
-    float f30 = b30 ? 0.0f : 1.0f;
-    float f31 = b31 ? 0.0f : 1.0f;
-    float f32 = b32 ? 0.0f : 1.0f;
-    float f33 = b33 ? 0.0f : 1.0f;
-    float f34 = b34 ? 0.0f : 1.0f;
-    float f36 = b36 ? 0.0f : 1.0f;
-    float f35 = b35 ? 0.0f : 1.0f;
-    float f08 = b08 ? 0.0f : 1.0f;
-    float f09 = b09 ? 0.0f : 1.0f;
-    float f10 = b10 ? 0.0f : 1.0f;
-    float f11 = b11 ? 0.0f : 1.0f;
-    float f12 = b12 ? 0.0f : 1.0f;
-    float f14 = b14 ? 0.0f : 1.0f;
-    float f13 = b13 ? 0.0f : 1.0f;
-
-    if (conditionA) {
-      f16 /= 1000.0f;
-      f17 /= 1000.0f;
-      f18 /= 1000.0f;
-      f19 /= 1000.0f;
-      f20 /= 1000.0f;
-      f21 /= 1000.0f;
-      f15 /= 1000.0f;
-      f08 /= 1000.0f;
-      f09 /= 1000.0f;
-      f10 /= 1000.0f;
-      f11 /= 1000.0f;
-      f12 /= 1000.0f;
-      f30 /= 1000.0f;
-      f31 /= 1000.0f;
-      f32 /= 1000.0f;
-      f33 /= 1000.0f;
-      f34 /= 1000.0f;
-      f01 /= 1000.0f;
-      f02 /= 1000.0f;
-      f03 /= 1000.0f;
-      f04 /= 1000.0f;
-      f05 /= 1000.0f;
-      f23 /= 1000.0f;
-      f24 /= 1000.0f;
-      f25 /= 1000.0f;
-      f26 /= 1000.0f;
-      f27 /= 1000.0f;
-      f22 /= 1000.0f;
-      f00 /= 1000.0f;
-      f14 /= 1000.0f;
-      f13 /= 1000.0f;
-      f36 /= 1000.0f;
-      f35 /= 1000.0f;
-      f07 /= 1000.0f;
-      f06 /= 1000.0f;
-      f29 /= 1000.0f;
-      f28 /= 1000.0f;
-    }
-    // The parallel move that used to exhaust the ARM64 parallel move
-    // resolver's scratch register pool (provided by VIXL) was in the
-    // "else" branch of the following condition generated by ART's
-    // compiler.
-    if (conditionB) {
-      f16 /= 100.0f;
-      f17 /= 100.0f;
-      f18 /= 100.0f;
-      f19 /= 100.0f;
-      f20 /= 100.0f;
-      f21 /= 100.0f;
-      f15 /= 100.0f;
-      f08 /= 100.0f;
-      f09 /= 100.0f;
-      f10 /= 100.0f;
-      f11 /= 100.0f;
-      f12 /= 100.0f;
-      f30 /= 100.0f;
-      f31 /= 100.0f;
-      f32 /= 100.0f;
-      f33 /= 100.0f;
-      f34 /= 100.0f;
-      f01 /= 100.0f;
-      f02 /= 100.0f;
-      f03 /= 100.0f;
-      f04 /= 100.0f;
-      f05 /= 100.0f;
-      f23 /= 100.0f;
-      f24 /= 100.0f;
-      f25 /= 100.0f;
-      f26 /= 100.0f;
-      f27 /= 100.0f;
-      f22 /= 100.0f;
-      f00 /= 100.0f;
-      f14 /= 100.0f;
-      f13 /= 100.0f;
-      f36 /= 100.0f;
-      f35 /= 100.0f;
-      f07 /= 100.0f;
-      f06 /= 100.0f;
-      f29 /= 100.0f;
-      f28 /= 100.0f;
-    }
-    if (conditionC) {
-      f16 /= 12.0f;
-      f17 /= 12.0f;
-      f18 /= 12.0f;
-      f19 /= 12.0f;
-      f20 /= 12.0f;
-      f21 /= 12.0f;
-      f15 /= 12.0f;
-      f08 /= 12.0f;
-      f09 /= 12.0f;
-      f10 /= 12.0f;
-      f11 /= 12.0f;
-      f12 /= 12.0f;
-      f30 /= 12.0f;
-      f31 /= 12.0f;
-      f32 /= 12.0f;
-      f33 /= 12.0f;
-      f34 /= 12.0f;
-      f01 /= 12.0f;
-      f02 /= 12.0f;
-      f03 /= 12.0f;
-      f04 /= 12.0f;
-      f05 /= 12.0f;
-      f23 /= 12.0f;
-      f24 /= 12.0f;
-      f25 /= 12.0f;
-      f26 /= 12.0f;
-      f27 /= 12.0f;
-      f22 /= 12.0f;
-      f00 /= 12.0f;
-      f14 /= 12.0f;
-      f13 /= 12.0f;
-      f36 /= 12.0f;
-      f35 /= 12.0f;
-      f07 /= 12.0f;
-      f06 /= 12.0f;
-      f29 /= 12.0f;
-      f28 /= 12.0f;
-    }
-    float s = 0.0f;
-    s = ((float) Math.round(100.0f * s)) / 100.0f;
-    String res = s + r;
-  }
-
   public static void main(String[] args) {
-    Main main = new Main();
-    main.test();
     System.out.println("passed");
   }
 }
diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java
index abf8c27..4b72656 100644
--- a/test/679-checker-minmax/src/Main.java
+++ b/test/679-checker-minmax/src/Main.java
@@ -37,6 +37,13 @@
   //
   /// CHECK-START: int Main.minI(int) instruction_simplifier (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
+  //
+  /// CHECK-START-ARM64: int Main.minI(int) disassembly (after)
+  /// CHECK-NOT:              mov {{w\d+}}, #0x14
+  /// CHECK:                  cmp {{w\d+}}, #0x14
+  //  Check that the constant generation was handled by VIXL.
+  /// CHECK:                  mov w16, #0x14
+  /// CHECK:                  csel {{w\d+}}, {{w\d+}}, w16, lt
   public static int minI(int a) {
     return Math.min(a, 20);
   }
@@ -55,6 +62,13 @@
   //
   /// CHECK-START: long Main.minL(long) instruction_simplifier (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
+  //
+  /// CHECK-START-ARM64: long Main.minL(long) disassembly (after)
+  /// CHECK-NOT:              mov {{x\d+}}, #0x14
+  /// CHECK:                  cmp {{x\d+}}, #0x14
+  //  Check that the constant generation was handled by VIXL.
+  /// CHECK:                  mov x16, #0x14
+  /// CHECK:                  csel {{x\d+}}, {{x\d+}}, x16, lt
   public static long minL(long a) {
     return Math.min(a, 20L);
   }
@@ -73,6 +87,13 @@
   //
   /// CHECK-START: int Main.maxI(int) instruction_simplifier (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
+  //
+  /// CHECK-START-ARM64: int Main.maxI(int) disassembly (after)
+  /// CHECK-NOT:              mov {{w\d+}}, #0x14
+  /// CHECK:                  cmp {{w\d+}}, #0x14
+  //  Check that the constant generation was handled by VIXL.
+  /// CHECK:                  mov w16, #0x14
+  /// CHECK:                  csel {{w\d+}}, {{w\d+}}, w16, gt
   public static int maxI(int a) {
     return Math.max(a, 20);
   }
@@ -91,11 +112,166 @@
   //
   /// CHECK-START: long Main.maxL(long) instruction_simplifier (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
+  //
+  /// CHECK-START-ARM64: long Main.maxL(long) disassembly (after)
+  /// CHECK-NOT:              mov {{x\d+}}, #0x14
+  /// CHECK:                  cmp {{x\d+}}, #0x14
+  //  Check that the constant generation was handled by VIXL.
+  /// CHECK:                  mov x16, #0x14
+  /// CHECK:                  csel {{x\d+}}, {{x\d+}}, x16, gt
   public static long maxL(long a) {
     return Math.max(a, 20L);
   }
 
   //
+  // Special Cases
+  //
+
+  /// CHECK-START-ARM64: int Main.minIntConstantZero(int) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{w\d+}}, #0x0
+  /// CHECK:            cmp {{w\d+}}, #0x0 (0)
+  /// CHECK:            csel {{w\d+}}, {{w\d+}}, wzr, lt
+  /// CHECK:            ret
+  public static int minIntConstantZero(int a) {
+    return Math.min(a, 0);
+  }
+
+  /// CHECK-START-ARM64: int Main.minIntConstantOne(int) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{w\d+}}, #0x1
+  /// CHECK:            cmp {{w\d+}}, #0x1 (1)
+  /// CHECK:            csinc {{w\d+}}, {{w\d+}}, wzr, lt
+  /// CHECK:            ret
+  public static int minIntConstantOne(int a) {
+    return Math.min(a, 1);
+  }
+
+  /// CHECK-START-ARM64: int Main.minIntConstantMinusOne(int) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{w\d+}}, #0xffffffff
+  /// CHECK:            cmn {{w\d+}}, #0x1 (1)
+  /// CHECK:            csinv {{w\d+}}, {{w\d+}}, wzr, lt
+  /// CHECK:            ret
+  public static int minIntConstantMinusOne(int a) {
+    return Math.min(a, -1);
+  }
+
+  /// CHECK-START-ARM64: long Main.minLongConstantZero(long) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{x\d+}}, #0x0
+  /// CHECK:            cmp {{x\d+}}, #0x0 (0)
+  /// CHECK:            csel {{x\d+}}, {{x\d+}}, xzr, lt
+  /// CHECK:            ret
+  public static long minLongConstantZero(long a) {
+    return Math.min(a, 0L);
+  }
+
+  /// CHECK-START-ARM64: long Main.minLongConstantOne(long) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{x\d+}}, #0x1
+  /// CHECK:            cmp {{x\d+}}, #0x1 (1)
+  /// CHECK:            csinc {{x\d+}}, {{x\d+}}, xzr, lt
+  /// CHECK:            ret
+  public static long minLongConstantOne(long a) {
+    return Math.min(a, 1L);
+  }
+
+  /// CHECK-START-ARM64: long Main.minLongConstantMinusOne(long) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{x\d+}}, #0xffffffffffffffff
+  /// CHECK:            cmn {{x\d+}}, #0x1 (1)
+  /// CHECK:            csinv {{x\d+}}, {{x\d+}}, xzr, lt
+  /// CHECK:            ret
+  public static long minLongConstantMinusOne(long a) {
+    return Math.min(a, -1L);
+  }
+
+  /// CHECK-START-ARM64: int Main.maxIntConstantZero(int) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{w\d+}}, #0x0
+  /// CHECK:            cmp {{w\d+}}, #0x0 (0)
+  /// CHECK:            csel {{w\d+}}, {{w\d+}}, wzr, gt
+  /// CHECK:            ret
+  public static int maxIntConstantZero(int a) {
+    return Math.max(a, 0);
+  }
+
+  /// CHECK-START-ARM64: int Main.maxIntConstantOne(int) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{w\d+}}, #0x1
+  /// CHECK:            cmp {{w\d+}}, #0x1 (1)
+  /// CHECK:            csinc {{w\d+}}, {{w\d+}}, wzr, gt
+  /// CHECK:            ret
+  public static int maxIntConstantOne(int a) {
+    return Math.max(a, 1);
+  }
+
+  /// CHECK-START-ARM64: int Main.maxIntConstantMinusOne(int) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{w\d+}}, #0xffffffff
+  /// CHECK:            cmn {{w\d+}}, #0x1 (1)
+  /// CHECK:            csinv {{w\d+}}, {{w\d+}}, wzr, gt
+  /// CHECK:            ret
+  public static int maxIntConstantMinusOne(int a) {
+    return Math.max(a, -1);
+  }
+
+  /// CHECK-START-ARM64: int Main.maxIntLargeConstant(int) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK:            mov {{w\d+}}, #0x2001
+  /// CHECK:            cmp {{w\d+}}, {{w\d+}}
+  //  Check that constant generation was not handled by VIXL.
+  /// CHECK-NOT:        mov {{w\d+}}, #0x2001
+  /// CHECK:            csel {{w\d+}}, {{w\d+}}, {{w\d+}}, gt
+  /// CHECK:            ret
+  public static int maxIntLargeConstant(int a) {
+    return Math.max(a, 8193);
+  }
+
+  /// CHECK-START-ARM64: long Main.maxLongConstantZero(long) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{x\d+}}, #0x0
+  /// CHECK:            cmp {{x\d+}}, #0x0 (0)
+  /// CHECK:            csel {{x\d+}}, {{x\d+}}, xzr, gt
+  /// CHECK:            ret
+  public static long maxLongConstantZero(long a) {
+    return Math.max(a, 0L);
+  }
+
+  /// CHECK-START-ARM64: long Main.maxLongConstantOne(long) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{x\d+}}, #0x1
+  /// CHECK:            cmp {{x\d+}}, #0x1 (1)
+  /// CHECK:            csinc {{x\d+}}, {{x\d+}}, xzr, gt
+  /// CHECK:            ret
+  public static long maxLongConstantOne(long a) {
+    return Math.max(a, 1L);
+  }
+
+  /// CHECK-START-ARM64: long Main.maxLongConstantMinusOne(long) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK-NOT:        mov {{x\d+}}, #0xffffffffffffffff
+  /// CHECK:            cmn {{x\d+}}, #0x1 (1)
+  /// CHECK:            csinv {{x\d+}}, {{x\d+}}, xzr, gt
+  /// CHECK:            ret
+  public static long maxLongConstantMinusOne(long a) {
+    return Math.max(a, -1L);
+  }
+
+  /// CHECK-START-ARM64: long Main.maxLongLargeConstant(long) disassembly (after)
+  /// CHECK-NOT:        InvokeStaticOrDirect
+  /// CHECK:            mov {{x\d+}}, #0x2001
+  /// CHECK:            cmp {{x\d+}}, {{x\d+}}
+  //  Check that constant generation was not handled by VIXL.
+  /// CHECK-NOT:        mov {{x\d+}}, #0x2001
+  /// CHECK:            csel {{x\d+}}, {{x\d+}}, {{x\d+}}, gt
+  /// CHECK:            ret
+  public static long maxLongLargeConstant(long a) {
+    return Math.max(a, 8193L);
+  }
+
+  //
   // Different types.
   //
 
@@ -538,12 +714,40 @@
     // Intrinsics.
     expectEquals(10, minI(10));
     expectEquals(20, minI(25));
+    expectEquals(-1, minIntConstantZero(-1));
+    expectEquals(0, minIntConstantZero(1));
+    expectEquals(0, minIntConstantOne(0));
+    expectEquals(1, minIntConstantOne(2));
+    expectEquals(-2, minIntConstantMinusOne(-2));
+    expectEquals(-1, minIntConstantMinusOne(0));
     expectEquals(10L, minL(10L));
     expectEquals(20L, minL(25L));
+    expectEquals(-1L, minLongConstantZero(-1L));
+    expectEquals(0L, minLongConstantZero(1L));
+    expectEquals(0L, minLongConstantOne(0L));
+    expectEquals(1L, minLongConstantOne(2L));
+    expectEquals(-2L, minLongConstantMinusOne(-2L));
+    expectEquals(-1L, minLongConstantMinusOne(0L));
     expectEquals(20, maxI(10));
     expectEquals(25, maxI(25));
+    expectEquals(0, maxIntConstantZero(-1));
+    expectEquals(1, maxIntConstantZero(1));
+    expectEquals(1, maxIntConstantOne(0));
+    expectEquals(2, maxIntConstantOne(2));
+    expectEquals(-1, maxIntConstantMinusOne(-2));
+    expectEquals(0, maxIntConstantMinusOne(0));
+    expectEquals(8193, maxIntLargeConstant(8192));
+    expectEquals(9000, maxIntLargeConstant(9000));
     expectEquals(20L, maxL(10L));
     expectEquals(25L, maxL(25L));
+    expectEquals(0L, maxLongConstantZero(-1L));
+    expectEquals(1L, maxLongConstantZero(1L));
+    expectEquals(1L, maxLongConstantOne(0L));
+    expectEquals(2L, maxLongConstantOne(2L));
+    expectEquals(-1L, maxLongConstantMinusOne(-2L));
+    expectEquals(0L, maxLongConstantMinusOne(0L));
+    expectEquals(8193L, maxLongLargeConstant(8192L));
+    expectEquals(9000L, maxLongLargeConstant(9000L));
     // Types.
     expectEquals(10, min1(10, 20));
     expectEquals(10, min2(10, 20));
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 067e678..e8d4290 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -328,7 +328,7 @@
     if gdb_arg:
       options_all += ' --gdb-arg ' + gdb_arg
 
-  options_all += ' '.join(run_test_option)
+  options_all += ' ' + ' '.join(run_test_option)
 
   if runtime_option:
     for opt in runtime_option:
@@ -1010,11 +1010,11 @@
   if build:
     build_targets = ''
     if 'host' in _user_input_variants['target']:
-      build_targets += 'test-art-host-run-test-dependencies'
+      build_targets += 'test-art-host-run-test-dependencies '
     if 'target' in _user_input_variants['target']:
-      build_targets += 'test-art-target-run-test-dependencies'
+      build_targets += 'test-art-target-run-test-dependencies '
     if 'jvm' in _user_input_variants['target']:
-      build_targets += 'test-art-host-run-test-dependencies'
+      build_targets += 'test-art-host-run-test-dependencies '
     build_command = 'make'
     build_command += ' DX='
     build_command += ' -j'
diff --git a/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java
index d3fea48..6185dee 100644
--- a/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java
+++ b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java
@@ -17,9 +17,8 @@
 package com.android.ahat.dominators;
 
 import java.util.ArrayDeque;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Deque;
-import java.util.List;
 import java.util.Queue;
 
 /**
@@ -106,43 +105,133 @@
     // dominator of B.
     public long id;
 
-    // Upper bound on the id of this node's dominator.
-    // The true immediate dominator of this node must have id <= domid.
-    // This upper bound is slowly tightened as part of the dominators
-    // computation.
-    public long domid;
+    // The largest id of all nodes reachable from this node.
+    // If foo.id > this.maxReachableId, then foo is not reachable from this
+    // node.
+    public long maxReachableId;
+
+    // The set of ids of nodes that have references to this node.
+    public IdSet inRefIds = new IdSet();
 
     // The current candidate dominator for this node.
-    // Invariant: (domid < domS.id) implies this node is on the queue of
-    // nodes to be revisited.
+    // The true immediate dominator of this node must have id <= domS.id.
     public NodeS domS;
 
-    // A node with a reference to this node that is one step closer to the
-    // root than this node.
-    // Invariant: srcS.id < this.id
-    public NodeS srcS;
+    // The previous candidate dominator for this node.
+    // Invariant:
+    // * There are no nodes xS reachable from this node on a path of nodes
+    //   with increasing ids (not counting xS.id) for which
+    //   this.id > xS.domS.id > this.oldDomS.id.
+    // This ensures that when all nodes xS satisfy xS.domS == xS.oldDomS, we
+    // have found the true immediate dominator of each node.
+    //
+    // Note: We only use this field to tell if this node is scheduled to be
+    // revisited. We could replace it with a boolean to save space, but it
+    // probably doesn't save that much space and it's easier to explain the
+    // algorithm if we can refer to this field.
+    public NodeS oldDomS;
 
-    // The largest id of the nodes we have seen so far on a path from the root
-    // to this node. Used to keep track of which nodes we have already seen
-    // and avoid processing them again.
-    public long seenid;
+    // The set of nodes that this node is the candidate immediate dominator
+    // of. More precisely, the set of nodes xS such that xS.domS == this.
+    public NodeSet dominated = new NodeSet();
 
-    // The set of nodes X reachable by 'this' on a path of nodes from the
-    // root with increasing ids (possibly excluding X) that this node does not
-    // dominate (this.id > X.domid).
-    // We can use a List instead of a Set for this because we guarentee based
-    // on seenid that we don't add the same node more than once to the list.
-    public List<NodeS> undom = new ArrayList<NodeS>();
+    // The set of nodes that this node is the old candidate immediate
+    // dominator of that need to be revisited. Specifically, the set of nodes
+    // xS such that:
+    //   xS.oldDomS == this && xS.oldDomS != xS.domS.
+    //
+    // The empty set is represented as null instead of an empty NodeSet to
+    // save memory.
+    // Invariant:
+    //   If revisit != null, this node is on the global list of nodes to be
+    //   revisited.
+    public NodeSet revisit = null;
   }
 
-  private static class Link {
-    public NodeS srcS;
-    public Node dst;
+  // A collection of node ids.
+  private static class IdSet {
+    private int size = 0;
+    private long[] ids = new long[4];
 
+    // Adds an id to the set.
+    public void add(long id) {
+      if (size == ids.length) {
+        ids = Arrays.copyOf(ids, size * 2);
+      }
+      ids[size++] = id;
+    }
+
+    // Returns the most recent id added to the set. Behavior is undefined if
+    // the set is empty.
+    public long last() {
+      assert size != 0;
+      return ids[size - 1];
+    }
+
+    // Returns true if the set contains an id in the range [low, high]
+    // inclusive, false otherwise.
+    public boolean hasIdInRange(long low, long high) {
+      for (int i = 0; i < size; ++i) {
+        if (low <= ids[i] && ids[i] <= high) {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
+  // An unordered set of nodes data structure supporting efficient iteration
+  // over elements. The bulk of the time spent in the dominators algorithm is
+  // iterating over these sets. Using an array to store the set provides
+  // noticable performance improvements over ArrayList or a linked list.
+  private static class NodeSet {
+    public int size = 0;
+    public NodeS[] nodes = new NodeS[4];
+
+    public void add(NodeS nodeS) {
+      if (size == nodes.length) {
+        nodes = Arrays.copyOf(nodes, size * 2);
+      }
+      nodes[size++] = nodeS;
+    }
+
+    public void remove(NodeS nodeS) {
+      for (int i = 0; i < size; ++i) {
+        if (nodes[i] == nodeS) {
+          remove(i);
+          break;
+        }
+      }
+    }
+
+    public void remove(int index) {
+      nodes[index] = nodes[--size];
+      nodes[size] = null;
+    }
+  }
+
+  // A reference from a source node to a destination node to be processed
+  // during the initial depth-first traversal of nodes.
+  //
+  // Also used as a marker to indicate when the depth-first traversal has been
+  // completed for a node. In that case, srcS is the node depth-first
+  // traversal has been completed for, and dst will be set to null.
+  private static class Link {
+    public final NodeS srcS;
+    public final Node dst;
+
+    // Constructor for a reference from srcS to dst.
     public Link(NodeS srcS, Node dst) {
       this.srcS = srcS;
       this.dst = dst;
     }
+
+    // Constructor for a marker indicating depth-first traversal has been
+    // completed for srcS.
+    public Link(NodeS srcS) {
+      this.srcS = srcS;
+      this.dst = null;
+    }
   }
 
   /**
@@ -158,16 +247,10 @@
   public static void computeDominators(Node root) {
     long id = 0;
 
-    // List of all nodes seen. We keep track of this here to update all the
-    // dominators once we are done.
-    List<NodeS> nodes = new ArrayList<NodeS>();
-
-    // The set of nodes N such that N.domid < N.domS.id. These nodes need
-    // to be revisisted because their dominator is clearly wrong.
+    // The set of nodes xS such that xS.revisit != null.
     // Use a Queue instead of a Set because performance will be better. We
-    // avoid adding nodes already on the queue by checking whether it was
-    // already true that N.domid < N.domS.id, in which case the node is
-    // already on the queue.
+    // avoid adding nodes already on the queue by checking
+    // xS == null before adding the node to the queue.
     Queue<NodeS> revisit = new ArrayDeque<NodeS>();
 
     // Set up the root node specially.
@@ -176,107 +259,166 @@
     rootS.id = id++;
     root.setDominatorsComputationState(rootS);
 
-    // 1. Do a depth first search of the nodes, label them with ids and come
-    // up with intial candidate dominators for them.
     Deque<Link> dfs = new ArrayDeque<Link>();
+    dfs.push(new Link(rootS));
     for (Node child : root.getReferencesForDominators()) {
       dfs.push(new Link(rootS, child));
     }
 
+    // 1. Do a depth first search of the nodes, label them with ids and come
+    // up with initial candidate dominators for them.
     while (!dfs.isEmpty()) {
       Link link = dfs.pop();
-      NodeS dstS = (NodeS)link.dst.getDominatorsComputationState();
-      if (dstS == null) {
-        // This is the first time we have seen the node. The candidate
-        // dominator is link src.
-        dstS = new NodeS();
-        dstS.node = link.dst;
-        dstS.id = id++;
-        dstS.domid = link.srcS.id;
-        dstS.domS = link.srcS;
-        dstS.srcS = link.srcS;
-        dstS.seenid = dstS.domid;
-        nodes.add(dstS);
-        link.dst.setDominatorsComputationState(dstS);
 
-        for (Node child : link.dst.getReferencesForDominators()) {
-          dfs.push(new Link(dstS, child));
-        }
+      if (link.dst == null) {
+        // This is the marker link indicating we have now visited all
+        // nodes reachable from link.srcS.
+        link.srcS.maxReachableId = id - 1;
       } else {
-        // We have seen the node already. Update the state based on the new
-        // potential dominator.
-        NodeS srcS = link.srcS;
-        boolean revisiting = dstS.domid < dstS.domS.id;
+        NodeS dstS = (NodeS)link.dst.getDominatorsComputationState();
+        if (dstS == null) {
+          // We are seeing the destination node for the first time.
+          // The candidate dominator is the source node.
+          dstS = new NodeS();
+          link.dst.setDominatorsComputationState(dstS);
 
-        while (srcS.id > dstS.seenid) {
-          srcS.undom.add(dstS);
-          srcS = srcS.srcS;
-        }
-        dstS.seenid = link.srcS.id;
+          dstS.node = link.dst;
+          dstS.id = id++;
+          dstS.inRefIds.add(link.srcS.id);
+          dstS.domS = link.srcS;
+          dstS.domS.dominated.add(dstS);
+          dstS.oldDomS = link.srcS;
 
-        if (srcS.id < dstS.domid) {
-          // In this case, dstS.domid must be wrong, because we just found a
-          // path to dstS that does not go through dstS.domid:
-          // All nodes from root to srcS have id < domid, and all nodes from
-          // srcS to dstS had id > domid, so dstS.domid cannot be on this path
-          // from root to dstS.
-          dstS.domid = srcS.id;
-          if (!revisiting) {
-            revisit.add(dstS);
+          dfs.push(new Link(dstS));
+          for (Node child : link.dst.getReferencesForDominators()) {
+            dfs.push(new Link(dstS, child));
+          }
+        } else {
+          // We have seen the destination node before. Update the state based
+          // on the new potential dominator.
+          long seenid = dstS.inRefIds.last();
+          dstS.inRefIds.add(link.srcS.id);
+
+          // Go up the dominator chain until we reach a node we haven't already
+          // seen with a path to dstS.
+          NodeS xS = link.srcS;
+          while (xS.id > seenid) {
+            xS = xS.domS;
+          }
+
+          // The new dominator for dstS must have an id less than the node we
+          // just reached. Pull the dominator for dstS up its dominator
+          // chain until we find a suitable new dominator for dstS.
+          long domid = xS.id;
+          if (dstS.domS.id > domid) {
+            // Mark the node as needing to be revisited.
+            if (dstS.domS == dstS.oldDomS) {
+              if (dstS.oldDomS.revisit == null) {
+                dstS.oldDomS.revisit = new NodeSet();
+                revisit.add(dstS.oldDomS);
+              }
+              dstS.oldDomS.revisit.add(dstS);
+            }
+
+            // Update the node's candidate dominator.
+            dstS.domS.dominated.remove(dstS);
+            do {
+              dstS.domS = dstS.domS.domS;
+            } while (dstS.domS.id > domid);
+            dstS.domS.dominated.add(dstS);
           }
         }
       }
     }
 
-    // 2. Continue revisiting nodes until they all satisfy the requirement
-    // that domS.id <= domid.
+    // 2. Continue revisiting nodes until every node satisfies the requirement
+    // that domS.id == oldDomS.id.
+    while (!revisit.isEmpty()) {
+      NodeS oldDomS = revisit.poll();
+      assert oldDomS.revisit != null;
+
+      NodeSet nodes = oldDomS.revisit;
+      oldDomS.revisit = null;
+
+      // Search for pairs of nodes nodeS, xS for which
+      //    nodeS.id > xS.domS.id > nodeS.oldDomS.id
+      // and there is a path of nodes with increasing ids from nodeS to xS.
+      // In that case, xS.domS must be wrong, because there is a path to xS
+      // from the root that does not go through xS.domS:
+      // * There is a path from the root to nodeS.oldDomS that doesn't go
+      //   through xS.domS. Otherwise xS.domS would be a dominator of
+      //   nodeS.oldDomS, but it can't be because xS.domS.id > nodeS.oldDomS.id.
+      // * There is a path from nodeS.oldDomS to nodeS that doesn't go through
+      //   xS.domS, because xS.domS is not a dominator of nodeS.
+      // * There is a path from nodeS to xS that doesn't go through xS.domS,
+      //   because we have a path of increasing ids from nodeS to xS, none of
+      //   which can have an id smaller than nodeS as xS.domS does.
+      for (int i = 0; i < oldDomS.dominated.size; ++i) {
+        NodeS xS = oldDomS.dominated.nodes[i];
+        for (int j = 0; j < nodes.size; ++j) {
+          NodeS nodeS = nodes.nodes[j];
+          assert nodeS.oldDomS == oldDomS;
+          if (isReachableAscending(nodeS, xS)) {
+            // Update the dominator for xS.
+            if (xS.domS == xS.oldDomS) {
+              if (xS.oldDomS.revisit == null) {
+                xS.oldDomS.revisit = new NodeSet();
+                revisit.add(xS.oldDomS);
+              }
+              xS.oldDomS.revisit.add(xS);
+            }
+            oldDomS.dominated.remove(i--);
+            xS.domS = nodeS.domS;
+            xS.domS.dominated.add(xS);
+            break;
+          }
+        }
+      }
+
+      // We can now safely update oldDomS for each of the nodes nodeS while
+      // preserving the oldDomS invariant.
+      for (int i = 0; i < nodes.size; ++i) {
+        NodeS nodeS = nodes.nodes[i];
+        nodeS.oldDomS = oldDomS.oldDomS;
+        if (nodeS.oldDomS != nodeS.domS) {
+          if (nodeS.oldDomS.revisit == null) {
+            nodeS.oldDomS.revisit = new NodeSet();
+            revisit.add(nodeS.oldDomS);
+          }
+          nodeS.oldDomS.revisit.add(nodeS);
+        }
+      }
+    }
+
+    // 3. We have figured out the correct dominator for each node. Notify the
+    // user of the results by doing one last traversal of the nodes.
+    assert revisit.isEmpty();
+    revisit.add(rootS);
     while (!revisit.isEmpty()) {
       NodeS nodeS = revisit.poll();
-      NodeS domS = nodeS.domS;
-      assert nodeS.domid < domS.id;
-      while (domS.id > nodeS.domid) {
-        if (domS.domS.id < nodeS.domid) {
-          // In this case, nodeS.domid must be wrong, because there is a path
-          // from root to nodeS that does not go through nodeS.domid:
-          //  * We can go from root to domS without going through nodeS.domid,
-          //    because otherwise nodeS.domid would dominate domS, not
-          //    domS.domS.
-          //  * We can go from domS to nodeS without going through nodeS.domid
-          //    because we know nodeS is reachable from domS on a path of nodes
-          //    with increases ids, which cannot include nodeS.domid, which
-          //    has a smaller id than domS.
-          nodeS.domid = domS.domS.id;
-        }
-        domS.undom.add(nodeS);
-        domS = domS.srcS;
-      }
-      nodeS.domS = domS;
-      nodeS.domid = domS.id;
-
-      for (NodeS xS : nodeS.undom) {
-        if (domS.id < xS.domid) {
-          // In this case, xS.domid must be wrong, because there is a path
-          // from the root to xX that does not go through xS.domid:
-          //  * We can go from root to nodeS without going through xS.domid,
-          //    because otherwise xS.domid would dominate nodeS, not domS.
-          //  * We can go from nodeS to xS without going through xS.domid
-          //    because we know xS is reachable from nodeS on a path of nodes
-          //    with increasing ids, which cannot include xS.domid, which has
-          //    a smaller id than nodeS.
-          boolean revisiting = xS.domid < xS.domS.id;
-          xS.domid = domS.id;
-          if (!revisiting) {
-            revisit.add(xS);
-          }
-        }
-      }
-    }
-
-    // 3. Update the dominators of the nodes.
-    root.setDominatorsComputationState(null);
-    for (NodeS nodeS : nodes) {
-      nodeS.node.setDominator(nodeS.domS.node);
+      assert nodeS.domS == nodeS.oldDomS;
+      assert nodeS.revisit == null;
       nodeS.node.setDominatorsComputationState(null);
+      for (int i = 0; i < nodeS.dominated.size; ++i) {
+        NodeS xS = nodeS.dominated.nodes[i];
+        xS.node.setDominator(nodeS.node);
+        revisit.add(xS);
+      }
     }
   }
+
+  // Returns true if there is a path from srcS to dstS of nodes with ascending
+  // ids (not including dstS.id).
+  private static boolean isReachableAscending(NodeS srcS, NodeS dstS) {
+    if (dstS.id < srcS.id) {
+      // The first time we saw dstS was before we saw srcS. See if srcS is on
+      // the source chain for any nodes with direct references to dstS.
+      return dstS.inRefIds.hasIdInRange(srcS.id, srcS.maxReachableId);
+    }
+
+    // Otherwise dstS is only reachable from srcS on a node with ascending ids
+    // if it was visited for the first time while performing the depth-first
+    // traversal of srcS.
+    return dstS.id <= srcS.maxReachableId;
+  }
 }
diff --git a/tools/ahat/src/test/com/android/ahat/DominatorsTest.java b/tools/ahat/src/test/com/android/ahat/DominatorsTest.java
index 0424e10..d9af363 100644
--- a/tools/ahat/src/test/com/android/ahat/DominatorsTest.java
+++ b/tools/ahat/src/test/com/android/ahat/DominatorsTest.java
@@ -295,4 +295,35 @@
     assertEquals(p, d.dominator);
     assertEquals(p, e.dominator);
   }
+
+  @Test
+  public void twiceRevisit() {
+    //       /---->---\
+    //      /     /--> f -->-\
+    // --> a --> b -->--x---> c --> d
+    //            \----------->----/
+    // A regression test for a bug where we failed to ever revisit a node more
+    // than once. The node c is revisited a first time to bring its dominator
+    // up to b. c needs to be revisited again after the dominator for f is
+    // pulled up to a, and that revisit of c is necessary to ensure the
+    // dominator for d is pulled up to a.
+    Node a = new Node("a");
+    Node b = new Node("b");
+    Node x = new Node("x");
+    Node c = new Node("c");
+    Node d = new Node("d");
+    Node f = new Node("f");
+    a.depends = Arrays.asList(f, b);
+    b.depends = Arrays.asList(f, d, x);
+    x.depends = Arrays.asList(c);
+    c.depends = Arrays.asList(d);
+    f.depends = Arrays.asList(c);
+
+    a.computeDominators();
+    assertEquals(a, b.dominator);
+    assertEquals(b, x.dominator);
+    assertEquals(a, c.dominator);
+    assertEquals(a, d.dominator);
+    assertEquals(a, f.dominator);
+  }
 }
diff --git a/tools/art b/tools/art
index 781ee2f..62df7eb 100644
--- a/tools/art
+++ b/tools/art
@@ -22,6 +22,7 @@
 LAUNCH_WRAPPER=
 LIBART=libart.so
 JIT_PROFILE="no"
+ALLOW_DEFAULT_JDWP="no"
 VERBOSE="no"
 CLEAN_OAT_FILES="yes"
 EXTRA_OPTIONS=()
@@ -90,6 +91,8 @@
   --profile                Run with profiling, then run using profile data.
   --verbose                Run script verbosely.
   --no-clean               Don't cleanup oat directories.
+  --allow-default-jdwp     Don't automatically put in -XjdwpProvider:none.
+                           You probably do not want this.
 
 The ART_OPTIONS are passed directly to the Android Runtime.
 
@@ -306,6 +309,9 @@
   --no-clean)
     CLEAN_OAT_FILES="no"
     ;;
+  --allow-default-jdwp)
+    ALLOW_DEFAULT_JDWP="yes"
+    ;;
   --*)
     echo "unknown option: $1" 1>&2
     usage
@@ -357,6 +363,10 @@
   EXTRA_OPTIONS+=(-Xcompiler-option --generate-debug-info)
 fi
 
+if [ "$ALLOW_DEFAULT_JDWP" = "no" ]; then
+  EXTRA_OPTIONS+=(-XjdwpProvider:none)
+fi
+
 if [ "$JIT_PROFILE" = "yes" ]; then
   # Create the profile. The runtime expects profiles to be created before
   # execution.
diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh
index ca5219a..694c739 100755
--- a/tools/cleanup-buildbot-device.sh
+++ b/tools/cleanup-buildbot-device.sh
@@ -28,33 +28,16 @@
     exit 1
   fi
 
-  echo -e "${green}Clean up /system in chroot${nc}"
-  # Remove all files under /system except the potential property_contexts file.
-  #
-  # The current ART Buildbot set-up runs the "setup device" step
-  # (performed by script tools/setup-buildbot-device.sh) before the
-  # "device cleanup" step (implemented by this script). As
-  # property_contexts file aliases are created during the former step,
-  # we need this exception to prevent the property_contexts file under
-  # /system in the chroot from being removed by the latter step.
-  #
-  # TODO: Reorder ART Buildbot steps so that "device cleanup" happens
-  # before "setup device" and remove this special case.
-  adb shell test -d "$ART_TEST_CHROOT/system" \
-    "&&" find "$ART_TEST_CHROOT/system" \
-      ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \
-      ! -type d \
-      -exec rm -f \{\} +
+  if adb shell test -d "$ART_TEST_CHROOT"; then
+    echo -e "${green}Remove entire /system directory from chroot directory${nc}"
+    adb shell rm -rf "$ART_TEST_CHROOT/system"
 
-  echo -e "${green}Clean up some subdirs in /data in chroot${nc}"
-  adb shell rm -rf \
-    "$ART_TEST_CHROOT/data/local/tmp/*" \
-    "$ART_TEST_CHROOT/data/art-test" \
-    "$ART_TEST_CHROOT/data/nativetest" \
-    "$ART_TEST_CHROOT/data/nativetest64" \
-    "$ART_TEST_CHROOT/data/run-test" \
-    "$ART_TEST_CHROOT/data/dalvik-cache/*" \
-    "$ART_TEST_CHROOT/data/misc/trace/*"
+    echo -e "${green}Remove entire /data directory from chroot directory${nc}"
+    adb shell rm -rf "$ART_TEST_CHROOT/data"
+
+    echo -e "${green}Remove entire chroot directory${nc}"
+    adb shell rmdir "$ART_TEST_CHROOT" || adb shell ls -la "$ART_TEST_CHROOT"
+  fi
 else
   adb shell rm -rf \
     /data/local/tmp /data/art-test /data/nativetest /data/nativetest64 '/data/misc/trace/*'
diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc
index 244f45b..5299c9e 100644
--- a/tools/dexanalyze/dexanalyze_experiments.cc
+++ b/tools/dexanalyze/dexanalyze_experiments.cc
@@ -16,6 +16,7 @@
 
 #include "dexanalyze_experiments.h"
 
+#include <algorithm>
 #include <stdint.h>
 #include <inttypes.h>
 #include <iostream>
@@ -289,10 +290,21 @@
   for (ClassAccessor accessor : dex_file.GetClasses()) {
     std::set<size_t> unique_method_ids;
     std::set<size_t> unique_string_ids;
+    // Types accessed and count.
+    std::map<size_t, size_t> types_accessed;
+
+    // Map from dex field index -> class field index.
+    std::map<uint32_t, uint32_t> field_index_map_;
+    size_t current_idx = 0u;
+    for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) {
+      field_index_map_[field.GetIndex()] = current_idx++;
+    }
+
     for (const ClassAccessor::Method& method : accessor.GetMethods()) {
-      dex_code_bytes_ += method.GetInstructions().InsnsSizeInBytes();
+      CodeItemDataAccessor code_item(dex_file, method.GetCodeItem());
+      dex_code_bytes_ += code_item.InsnsSizeInBytes();
       unique_code_items.insert(method.GetCodeItemOffset());
-      for (const DexInstructionPcPair& inst : method.GetInstructions()) {
+      for (const DexInstructionPcPair& inst : code_item) {
         switch (inst->Opcode()) {
           case Instruction::CONST_STRING: {
             const dex::StringIndex string_index(inst->VRegB_21c());
@@ -300,6 +312,36 @@
             ++num_string_ids_from_code_;
             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:
+          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: {
+            const uint32_t receiver = inst->VRegB_22c();
+            const uint32_t dex_field_idx = inst->VRegC_22c();
+            const uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize();
+            ++field_receiver_[(receiver - first_arg_reg) & 0xF];
+            ++types_accessed[dex_file.GetFieldId(dex_field_idx).class_idx_.index_];
+            if (first_arg_reg == receiver) {
+              auto it = field_index_map_.find(dex_field_idx);
+              if (it != field_index_map_.end() && it->second < kMaxFieldIndex) {
+                ++field_index_[it->second];
+              } else {
+                ++field_index_other_;
+              }
+            }
+            ++field_output_[inst->VRegA_22c()];
+            break;
+          }
           case Instruction::CONST_STRING_JUMBO: {
             const dex::StringIndex string_index(inst->VRegB_31c());
             unique_string_ids.insert(string_index.index_);
@@ -310,6 +352,7 @@
           case Instruction::INVOKE_VIRTUAL:
           case Instruction::INVOKE_VIRTUAL_RANGE: {
             uint32_t method_idx = DexMethodIndex(inst.Inst());
+            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
               ++same_class_virtual_;
             }
@@ -320,6 +363,7 @@
           case Instruction::INVOKE_DIRECT:
           case Instruction::INVOKE_DIRECT_RANGE: {
             uint32_t method_idx = DexMethodIndex(inst.Inst());
+            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
               ++same_class_direct_;
             }
@@ -330,6 +374,7 @@
           case Instruction::INVOKE_STATIC:
           case Instruction::INVOKE_STATIC_RANGE: {
             uint32_t method_idx = DexMethodIndex(inst.Inst());
+            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
               ++same_class_static_;
             }
@@ -340,6 +385,7 @@
           case Instruction::INVOKE_INTERFACE:
           case Instruction::INVOKE_INTERFACE_RANGE: {
             uint32_t method_idx = DexMethodIndex(inst.Inst());
+            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
               ++same_class_interface_;
             }
@@ -350,6 +396,7 @@
           case Instruction::INVOKE_SUPER:
           case Instruction::INVOKE_SUPER_RANGE: {
             uint32_t method_idx = DexMethodIndex(inst.Inst());
+            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
               ++same_class_super_;
             }
@@ -357,18 +404,69 @@
             unique_method_ids.insert(method_idx);
             break;
           }
+          case Instruction::NEW_ARRAY: {
+            ++types_accessed[inst->VRegC_22c()];
+            break;
+          }
+          case Instruction::FILLED_NEW_ARRAY: {
+            ++types_accessed[inst->VRegB_35c()];
+            break;
+          }
+          case Instruction::FILLED_NEW_ARRAY_RANGE: {
+            ++types_accessed[inst->VRegB_3rc()];
+            break;
+          }
+          case Instruction::CONST_CLASS:
+          case Instruction::CHECK_CAST:
+          case Instruction::NEW_INSTANCE: {
+            ++types_accessed[inst->VRegB_21c()];
+            break;
+          }
+          case Instruction::INSTANCE_OF: {
+            ++types_accessed[inst->VRegB_21c()];
+            break;
+          }
           default:
             break;
         }
       }
     }
-    total_unique_method_idx_ += unique_method_ids.size();
+    // Count uses of top 16n.
+    std::vector<size_t> uses;
+    for (auto&& p : types_accessed) {
+      uses.push_back(p.second);
+    }
+    std::sort(uses.rbegin(), uses.rend());
+    for (size_t i = 0; i < uses.size(); ++i) {
+      if (i < 16) {
+        uses_top_types_ += uses[i];
+      }
+      uses_all_types_ += uses[i];
+    }
+    total_unique_types_ += types_accessed.size();
+    total_unique_method_ids_ += unique_method_ids.size();
     total_unique_string_ids_ += unique_string_ids.size();
   }
   total_unique_code_items_ += unique_code_items.size();
 }
 
 void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const {
+  const uint64_t fields_total = std::accumulate(field_receiver_, field_receiver_ + 16u, 0u);
+  for (size_t i = 0; i < 16; ++i) {
+    os << "receiver_reg=" << i << ": " << Percent(field_receiver_[i], fields_total) << "\n";
+  }
+  for (size_t i = 0; i < 16; ++i) {
+    os << "output_reg=" << i << ": " << Percent(field_output_[i], fields_total) << "\n";
+  }
+  const uint64_t fields_idx_total = std::accumulate(field_index_,
+                                                    field_index_ + kMaxFieldIndex,
+                                                    0u) + field_index_other_;
+  for (size_t i = 0; i < kMaxFieldIndex; ++i) {
+    os << "field_idx=" << i << ": " << Percent(field_index_[i], fields_idx_total) << "\n";
+  }
+  os << "field_idx=other: " << Percent(field_index_other_, fields_idx_total) << "\n";
+  os << "field_idx_savings=" << Percent((fields_idx_total - field_index_other_) * 2, total_size)
+     << "\n";
   os << "Num string ids: " << num_string_ids_ << "\n";
   os << "Num method ids: " << num_method_ids_ << "\n";
   os << "Num field ids: " << num_field_ids_ << "\n";
@@ -380,8 +478,10 @@
   os << "Interface same class: " << PercentDivide(same_class_interface_, total_interface_) << "\n";
   os << "Super same class: " << PercentDivide(same_class_super_, total_super_) << "\n";
   os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n";
-  os << "Unique(per class) method ids accessed from code: " << total_unique_method_idx_ << "\n";
-  os << "Unique(per class) string ids accessed from code: " << total_unique_string_ids_ << "\n";
+  os << "Avg unique methods accessed per class: "
+     << double(total_unique_method_ids_) / double(num_class_defs_) << "\n";
+  os << "Avg unique strings accessed per class: "
+     << double(total_unique_string_ids_) / double(num_class_defs_) << "\n";
   const size_t same_class_total =
       same_class_direct_ +
       same_class_virtual_ +
@@ -396,6 +496,9 @@
       total_super_;
   os << "Same class invokes: " << PercentDivide(same_class_total, other_class_total) << "\n";
   os << "Invokes from code: " << (same_class_total + other_class_total) << "\n";
+  os << "Type uses on top types: " << PercentDivide(uses_top_types_, uses_all_types_) << "\n";
+  os << "Type uses 1b savings: " << PercentDivide(uses_top_types_, total_size) << "\n";
+  os << "Total unique types accessed per class " << total_unique_types_ << "\n";
   os << "Total Dex code bytes: " << Percent(dex_code_bytes_, total_size) << "\n";
   os << "Total unique code items: " << total_unique_code_items_ << "\n";
   os << "Total Dex size: " << total_size << "\n";
@@ -420,7 +523,7 @@
           }
           case Instruction::MOVE_RESULT:
           case Instruction::MOVE_RESULT_OBJECT: {
-            if (space_for_out_arg) {
+            if (space_for_out_arg && inst->VRegA_11x() < 16) {
               move_result_savings_ += inst->SizeInCodeUnits() * 2;
             }
             break;
@@ -441,7 +544,7 @@
   }
   os << "Move result savings: " << Percent(move_result_savings_, total_size) << "\n";
   os << "One byte invoke savings: " << Percent(total, total_size) << "\n";
-  const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 3, 0u);
+  const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 2, 0u);
   os << "Low arg savings: " << Percent(low_arg_total * 2, total_size) << "\n";
 }
 
diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h
index 2be53d6..d3a9c59 100644
--- a/tools/dexanalyze/dexanalyze_experiments.h
+++ b/tools/dexanalyze/dexanalyze_experiments.h
@@ -17,6 +17,7 @@
 #ifndef ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_
 #define ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_
 
+#include <cstdint>
 #include <iosfwd>
 #include <memory>
 #include <set>
@@ -90,10 +91,16 @@
  private:
   // Total string ids loaded from dex code.
   size_t num_string_ids_from_code_ = 0;
-  size_t total_unique_method_idx_ = 0;
+  size_t total_unique_method_ids_ = 0;
   size_t total_unique_string_ids_ = 0;
   uint64_t total_unique_code_items_ = 0u;
 
+  static constexpr size_t kMaxFieldIndex = 32;
+  uint64_t field_index_[kMaxFieldIndex] = {};
+  uint64_t field_index_other_ = 0u;
+  uint64_t field_receiver_[16] = {};
+  uint64_t field_output_[16] = {};
+
   // Other dex ids.
   size_t dex_code_bytes_ = 0;
   size_t num_string_ids_ = 0;
@@ -113,6 +120,11 @@
   size_t total_interface_ = 0;
   size_t same_class_super_ = 0;
   size_t total_super_ = 0;
+
+  // Type usage.
+  uint64_t uses_top_types_ = 0u;
+  uint64_t uses_all_types_ = 0u;
+  uint64_t total_unique_types_ = 0u;
 };
 
 // Measure various code metrics including args per invoke-virtual, fill/spill move patterns.
diff --git a/tools/teardown-buildbot-device.sh b/tools/teardown-buildbot-device.sh
index bf14ca4..d25dd2b 100755
--- a/tools/teardown-buildbot-device.sh
+++ b/tools/teardown-buildbot-device.sh
@@ -25,62 +25,82 @@
 adb wait-for-device
 
 if [[ -n "$ART_TEST_CHROOT" ]]; then
-
-  # remove_filesystem_from_chroot DIR-IN-CHROOT FSTYPE REMOVE-DIR-IN-CHROOT
-  # -----------------------------------------------------------------------
-  # Unmount filesystem with type FSTYPE mounted in directory DIR-IN-CHROOT
-  # under the chroot directory.
-  # Remove DIR-IN-CHROOT under the chroot if REMOVE-DIR-IN-CHROOT is
-  # true.
-  remove_filesystem_from_chroot() {
-    local dir_in_chroot=$1
-    local fstype=$2
-    local remove_dir=$3
-    local dir="$ART_TEST_CHROOT/$dir_in_chroot"
-    adb shell test -d "$dir" \
-      && adb shell mount | grep -q "^$fstype on $dir type $fstype " \
-      && if adb shell umount "$dir"; then
-           $remove_dir && adb shell rmdir "$dir"
-         else
-           adb shell lsof "$dir"
-         fi
-  }
-
-  # Tear down the chroot dir.
-  echo -e "${green}Tear down the chroot dir in $ART_TEST_CHROOT${nc}"
-
   # Check that ART_TEST_CHROOT is correctly defined.
   [[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; }
 
-  # Remove /dev from chroot.
-  remove_filesystem_from_chroot dev tmpfs true
+  if adb shell test -d "$ART_TEST_CHROOT"; then
+    # Display users of the chroot dir.
 
-  # Remove /sys/kernel/debug from chroot.
-  # The /sys/kernel/debug directory under the chroot dir cannot be
-  # deleted, as it is part of the host device's /sys filesystem.
-  remove_filesystem_from_chroot sys/kernel/debug debugfs false
-  # Remove /sys from chroot.
-  remove_filesystem_from_chroot sys sysfs true
+    echo -e "${green}List open files under chroot dir $ART_TEST_CHROOT${nc}"
+    adb shell lsof | grep "$ART_TEST_CHROOT"
 
-  # Remove /proc from chroot.
-  remove_filesystem_from_chroot proc proc true
+    echo -e "${green}List processes running from binaries under chroot dir $ART_TEST_CHROOT${nc}"
+    for link in $(adb shell ls -d "/proc/*/root"); do
+      root=$(adb shell readlink "$link")
+      if [[ "x$root" = "x$ART_TEST_CHROOT" ]]; then
+        dir=$(dirname "$link")
+        pid=$(basename "$dir")
+        cmdline=$(adb shell cat "$dir"/cmdline | tr -d '\000')
+        echo "$cmdline (PID: $pid)"
+      fi
+    done
 
-  # Remove /etc from chroot.
-  adb shell rm -f "$ART_TEST_CHROOT/etc"
-  adb shell rm -rf "$ART_TEST_CHROOT/system/etc"
 
-  # Remove directories used for ART testing in chroot.
-  adb shell rm -rf "$ART_TEST_CHROOT/data/local/tmp"
-  adb shell rm -rf "$ART_TEST_CHROOT/data/dalvik-cache"
-  adb shell rm -rf "$ART_TEST_CHROOT/tmp"
+    # Tear down the chroot dir.
 
-  # Remove property_contexts file(s) from chroot.
-  property_context_files="/property_contexts \
-    /system/etc/selinux/plat_property_contexts \
-    /vendor/etc/selinux/nonplat_property_context \
-    /plat_property_contexts \
-    /nonplat_property_contexts"
-  for f in $property_context_files; do
-    adb shell rm -f "$ART_TEST_CHROOT$f"
-  done
+    echo -e "${green}Tear down the chroot set up in $ART_TEST_CHROOT${nc}"
+
+    # remove_filesystem_from_chroot DIR-IN-CHROOT FSTYPE REMOVE-DIR-IN-CHROOT
+    # -----------------------------------------------------------------------
+    # Unmount filesystem with type FSTYPE mounted in directory DIR-IN-CHROOT
+    # under the chroot directory.
+    # Remove DIR-IN-CHROOT under the chroot if REMOVE-DIR-IN-CHROOT is
+    # true.
+    remove_filesystem_from_chroot() {
+      local dir_in_chroot=$1
+      local fstype=$2
+      local remove_dir=$3
+      local dir="$ART_TEST_CHROOT/$dir_in_chroot"
+      adb shell test -d "$dir" \
+        && adb shell mount | grep -q "^$fstype on $dir type $fstype " \
+        && if adb shell umount "$dir"; then
+             $remove_dir && adb shell rmdir "$dir"
+           else
+             echo "Files still open in $dir:"
+             adb shell lsof | grep "$dir"
+           fi
+    }
+
+    # Remove /dev from chroot.
+    remove_filesystem_from_chroot dev tmpfs true
+
+    # Remove /sys/kernel/debug from chroot.
+    # The /sys/kernel/debug directory under the chroot dir cannot be
+    # deleted, as it is part of the host device's /sys filesystem.
+    remove_filesystem_from_chroot sys/kernel/debug debugfs false
+    # Remove /sys from chroot.
+    remove_filesystem_from_chroot sys sysfs true
+
+    # Remove /proc from chroot.
+    remove_filesystem_from_chroot proc proc true
+
+    # Remove /etc from chroot.
+    adb shell rm -f "$ART_TEST_CHROOT/etc"
+    adb shell rm -rf "$ART_TEST_CHROOT/system/etc"
+
+    # Remove directories used for ART testing in chroot.
+    adb shell rm -rf "$ART_TEST_CHROOT/data/local/tmp"
+    adb shell rm -rf "$ART_TEST_CHROOT/data/dalvik-cache"
+    adb shell rm -rf "$ART_TEST_CHROOT/tmp"
+
+    # Remove property_contexts file(s) from chroot.
+    property_context_files="/property_contexts \
+      /system/etc/selinux/plat_property_contexts \
+      /vendor/etc/selinux/nonplat_property_context \
+      /plat_property_contexts \
+      /nonplat_property_contexts"
+    for f in $property_context_files; do
+      adb shell rm -f "$ART_TEST_CHROOT$f"
+    done
+  fi
 fi
diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc
index 428304e..b147add 100644
--- a/tools/ti-fast/tifast.cc
+++ b/tools/ti-fast/tifast.cc
@@ -32,6 +32,10 @@
 
 namespace {
 
+// Special art ti-version number. We will use this as a fallback if we cannot get a regular JVMTI
+// env.
+static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+
 static void AddCapsForEvent(jvmtiEvent event, jvmtiCapabilities* caps) {
   switch (event) {
 #define DO_CASE(name, cap_name) \
@@ -135,6 +139,17 @@
   return res;
 }
 
+static jint SetupJvmtiEnv(JavaVM* vm, jvmtiEnv** jvmti) {
+  jint res = 0;
+  res = vm->GetEnv(reinterpret_cast<void**>(jvmti), JVMTI_VERSION_1_1);
+
+  if (res != JNI_OK || *jvmti == nullptr) {
+    LOG(ERROR) << "Unable to access JVMTI, error code " << res;
+    return vm->GetEnv(reinterpret_cast<void**>(jvmti), kArtTiVersion);
+  }
+  return res;
+}
+
 }  // namespace
 
 static jint AgentStart(JavaVM* vm,
@@ -142,14 +157,9 @@
                        void* reserved ATTRIBUTE_UNUSED) {
   jvmtiEnv* jvmti = nullptr;
   jvmtiError error = JVMTI_ERROR_NONE;
-  {
-    jint res = 0;
-    res = vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_1);
-
-    if (res != JNI_OK || jvmti == nullptr) {
-      LOG(ERROR) << "Unable to access JVMTI, error code " << res;
-      return JNI_ERR;
-    }
+  if (SetupJvmtiEnv(vm, &jvmti) != JNI_OK) {
+    LOG(ERROR) << "Could not get JVMTI env or ArtTiEnv!";
+    return JNI_ERR;
   }
   std::string args(options);
   bool is_log = false;