Merge "Sharpen HLoadClass from inliner."
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 2eb6fba..46f3358 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -49,6 +49,7 @@
         "optimizing/block_builder.cc",
         "optimizing/bounds_check_elimination.cc",
         "optimizing/builder.cc",
+        "optimizing/cha_guard_optimization.cc",
         "optimizing/code_generator.cc",
         "optimizing/code_generator_utils.cc",
         "optimizing/constant_folding.cc",
diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc
index 051125e..bfd485d 100644
--- a/compiler/dex/dex_to_dex_decompiler.cc
+++ b/compiler/dex/dex_to_dex_decompiler.cc
@@ -27,10 +27,13 @@
 
 class DexDecompiler {
  public:
-  DexDecompiler(const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info)
+  DexDecompiler(const DexFile::CodeItem& code_item,
+                const ArrayRef<const uint8_t>& quickened_info,
+                bool decompile_return_instruction)
     : code_item_(code_item),
       quickened_info_ptr_(quickened_info.data()),
-      quickened_info_end_(quickened_info.data() + quickened_info.size()) {}
+      quickened_info_end_(quickened_info.data() + quickened_info.size()),
+      decompile_return_instruction_(decompile_return_instruction) {}
 
   bool Decompile();
 
@@ -87,6 +90,7 @@
   const DexFile::CodeItem& code_item_;
   const uint8_t* quickened_info_ptr_;
   const uint8_t* const quickened_info_end_;
+  const bool decompile_return_instruction_;
 
   DISALLOW_COPY_AND_ASSIGN(DexDecompiler);
 };
@@ -102,7 +106,9 @@
 
     switch (inst->Opcode()) {
       case Instruction::RETURN_VOID_NO_BARRIER:
-        inst->SetOpcode(Instruction::RETURN_VOID);
+        if (decompile_return_instruction_) {
+          inst->SetOpcode(Instruction::RETURN_VOID);
+        }
         break;
 
       case Instruction::NOP:
@@ -189,8 +195,12 @@
 }
 
 bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
-                     const ArrayRef<const uint8_t>& quickened_info) {
-  DexDecompiler decompiler(code_item, quickened_info);
+                     const ArrayRef<const uint8_t>& quickened_info,
+                     bool decompile_return_instruction) {
+  if (quickened_info.size() == 0 && !decompile_return_instruction) {
+    return true;
+  }
+  DexDecompiler decompiler(code_item, quickened_info, decompile_return_instruction);
   return decompiler.Decompile();
 }
 
diff --git a/compiler/dex/dex_to_dex_decompiler.h b/compiler/dex/dex_to_dex_decompiler.h
index 5502ca2..b5d5b91 100644
--- a/compiler/dex/dex_to_dex_decompiler.h
+++ b/compiler/dex/dex_to_dex_decompiler.h
@@ -30,7 +30,8 @@
 // consistent with DexToDexCompiler, but we should really change it to
 // DexFile::CodeItem*.
 bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
-                     const ArrayRef<const uint8_t>& quickened_data);
+                     const ArrayRef<const uint8_t>& quickened_data,
+                     bool decompile_return_instruction);
 
 }  // namespace optimizer
 }  // namespace art
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index ea6c7a2..9a8d27c 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -102,7 +102,8 @@
         if (compiled_method != nullptr) {
           table = compiled_method->GetVmapTable();
         }
-        optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+        optimizer::ArtDecompileDEX(
+            *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
         it.Next();
       }
       while (it.HasNextVirtualMethod()) {
@@ -113,7 +114,8 @@
         if (compiled_method != nullptr) {
           table = compiled_method->GetVmapTable();
         }
-        optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+        optimizer::ArtDecompileDEX(
+            *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
         it.Next();
       }
       DCHECK(!it.HasNext());
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index d1a5c4d..ec1642e 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -435,7 +435,8 @@
 
 // In-place unquicken the given `dex_files` based on `quickening_info`.
 static void Unquicken(const std::vector<const DexFile*>& dex_files,
-                      const ArrayRef<const uint8_t>& quickening_info) {
+                      const ArrayRef<const uint8_t>& quickening_info,
+                      bool decompile_return_instruction) {
   const uint8_t* quickening_info_ptr = quickening_info.data();
   const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size();
   for (const DexFile* dex_file : dex_files) {
@@ -454,14 +455,14 @@
         it.Next();
       }
 
-      // Unquicken each method.
       while (it.HasNextDirectMethod()) {
         const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
         if (code_item != nullptr) {
           uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
           quickening_info_ptr += sizeof(uint32_t);
-          optimizer::ArtDecompileDEX(
-              *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+          optimizer::ArtDecompileDEX(*code_item,
+                                     ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+                                     decompile_return_instruction);
           quickening_info_ptr += quickening_size;
         }
         it.Next();
@@ -472,8 +473,9 @@
         if (code_item != nullptr) {
           uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
           quickening_info_ptr += sizeof(uint32_t);
-          optimizer::ArtDecompileDEX(
-              *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+          optimizer::ArtDecompileDEX(*code_item,
+                                     ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+                                     decompile_return_instruction);
           quickening_info_ptr += quickening_size;
         }
         it.Next();
@@ -493,7 +495,10 @@
     // if the boot image has changed. How exactly we'll know is under
     // experimentation.
     TimingLogger::ScopedTiming t("Unquicken", timings);
-    Unquicken(dex_files, vdex_file->GetQuickeningInfo());
+    // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening
+    // optimization does not depend on the boot image (the optimization relies on not
+    // having final fields in a class, which does not change for an app).
+    Unquicken(dex_files, vdex_file->GetQuickeningInfo(), /* decompile_return_instruction */ false);
     Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
         new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
   }
diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc
new file mode 100644
index 0000000..fe42301
--- /dev/null
+++ b/compiler/optimizing/cha_guard_optimization.cc
@@ -0,0 +1,253 @@
+/*
+ * 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.
+ */
+
+#include "cha_guard_optimization.h"
+
+namespace art {
+
+// Note we can only do CHA guard elimination/motion in a single pass, since
+// if a guard is not removed, another guard might be removed due to
+// the existence of the first guard. The first guard should not be further
+// removed in another pass. For example, due to further optimizations,
+// a receiver of a guard might turn out to be a parameter value, or defined at
+// a different site, which makes the guard removable as a result. However
+// it's not safe to remove the guard in another pass since another guard might
+// have been removed due to the existence of this guard.
+//
+// As a consequence, we decided not to rely on other passes to remove them
+// (such as GVN or instruction simplifier).
+
+class CHAGuardVisitor : HGraphVisitor {
+ public:
+  explicit CHAGuardVisitor(HGraph* graph)
+      : HGraphVisitor(graph),
+        block_has_cha_guard_(GetGraph()->GetBlocks().size(),
+                             0,
+                             graph->GetArena()->Adapter(kArenaAllocCHA)) {
+    number_of_guards_to_visit_ = GetGraph()->GetNumberOfCHAGuards();
+    DCHECK_NE(number_of_guards_to_visit_, 0u);
+    // Will recount number of guards during guard optimization.
+    GetGraph()->SetNumberOfCHAGuards(0);
+  }
+
+  void VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) OVERRIDE;
+
+  void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+
+ private:
+  void RemoveGuard(HShouldDeoptimizeFlag* flag);
+  // Return true if `flag` is removed.
+  bool OptimizeForParameter(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+  // Return true if `flag` is removed.
+  bool OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+  // Return true if `flag` is hoisted.
+  bool HoistGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+
+  // Record if each block has any CHA guard. It's updated during the
+  // reverse post order visit. Use int instead of bool since ArenaVector
+  // does not support bool.
+  ArenaVector<int> block_has_cha_guard_;
+
+  // The iterator that's being used for this visitor. Need it to manually
+  // advance the iterator due to removing/moving more than one instruction.
+  HInstructionIterator* instruction_iterator_;
+
+  // Used to short-circuit the pass when there is no more guards left to visit.
+  uint32_t number_of_guards_to_visit_;
+
+  DISALLOW_COPY_AND_ASSIGN(CHAGuardVisitor);
+};
+
+void CHAGuardVisitor::VisitBasicBlock(HBasicBlock* block) {
+  if (number_of_guards_to_visit_ == 0) {
+    return;
+  }
+  // Skip phis, just iterate through instructions.
+  HInstructionIterator it(block->GetInstructions());
+  instruction_iterator_ = &it;
+  for (; !it.Done(); it.Advance()) {
+    DCHECK(it.Current()->IsInBlock());
+    it.Current()->Accept(this);
+  }
+}
+
+void CHAGuardVisitor::RemoveGuard(HShouldDeoptimizeFlag* flag) {
+  HBasicBlock* block = flag->GetBlock();
+  HInstruction* compare = flag->GetNext();
+  DCHECK(compare->IsNotEqual());
+  HInstruction* deopt = compare->GetNext();
+  DCHECK(deopt->IsDeoptimize());
+
+  // Advance instruction iterator first before we remove the guard.
+  // We need to do it twice since we remove three instructions and the
+  // visitor is responsible for advancing it once.
+  instruction_iterator_->Advance();
+  instruction_iterator_->Advance();
+  block->RemoveInstruction(deopt);
+  block->RemoveInstruction(compare);
+  block->RemoveInstruction(flag);
+}
+
+bool CHAGuardVisitor::OptimizeForParameter(HShouldDeoptimizeFlag* flag,
+                                           HInstruction* receiver) {
+  // If some compiled code is invalidated by CHA due to class loading, the
+  // compiled code will not be entered anymore. So the very fact that the
+  // compiled code is invoked guarantees that a parameter receiver conforms
+  // to all the CHA devirtualization assumptions made by the compiled code,
+  // since all parameter receivers pre-exist any (potential) invalidation of
+  // the compiled code.
+  //
+  // TODO: allow more cases such as a phi whose inputs are all parameters.
+  if (receiver->IsParameterValue()) {
+    RemoveGuard(flag);
+    return true;
+  }
+  return false;
+}
+
+bool CHAGuardVisitor::OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag,
+                                                  HInstruction* receiver) {
+  // If there is another guard that dominates the current guard, and
+  // that guard is dominated by receiver's definition, then the current
+  // guard can be eliminated, since receiver must pre-exist that other
+  // guard, and passing that guard guarantees that receiver conforms to
+  // all the CHA devirtualization assumptions.
+  HBasicBlock* dominator = flag->GetBlock();
+  HBasicBlock* receiver_def_block = receiver->GetBlock();
+
+  // Complexity of the following algorithm:
+  // We potentially need to traverse the full dominator chain to receiver_def_block,
+  // plus a (partial) linear search within one block for each guard.
+  // So the worst case for each guard is bounded by the size of the
+  // biggest block plus the depth of the dominating tree.
+
+  while (dominator != receiver_def_block) {
+    if (block_has_cha_guard_[dominator->GetBlockId()] == 1) {
+      RemoveGuard(flag);
+      return true;
+    }
+    dominator = dominator->GetDominator();
+  }
+
+  // At this point dominator is the block where receiver is defined.
+  // We do a linear search within dominator to see if there is a guard after
+  // receiver's definition.
+  HInstruction* instruction;
+  if (dominator == flag->GetBlock()) {
+    // Flag and receiver are defined in the same block. Search backward from
+    // the current guard.
+    instruction = flag->GetPrevious();
+  } else {
+    // Search backward from the last instruction of that dominator.
+    instruction = dominator->GetLastInstruction();
+  }
+  while (instruction != receiver) {
+    if (instruction == nullptr) {
+      // receiver must be defined in this block, we didn't find it
+      // in the instruction list, so it must be a Phi.
+      DCHECK(receiver->IsPhi());
+      break;
+    }
+    if (instruction->IsShouldDeoptimizeFlag()) {
+      RemoveGuard(flag);
+      return true;
+    }
+    instruction = instruction->GetPrevious();
+  }
+  return false;
+}
+
+bool CHAGuardVisitor::HoistGuard(HShouldDeoptimizeFlag* flag,
+                                 HInstruction* receiver) {
+  // If receiver is loop invariant, we can hoist the guard out of the
+  // loop since passing a guard before entering the loop guarantees that
+  // receiver conforms to all the CHA devirtualization assumptions.
+  // We only hoist guards out of the inner loop since that offers most of the
+  // benefit and it might help remove other guards in the inner loop.
+  HBasicBlock* block = flag->GetBlock();
+  HLoopInformation* loop_info = block->GetLoopInformation();
+  if (loop_info != nullptr &&
+      !loop_info->IsIrreducible() &&
+      loop_info->IsDefinedOutOfTheLoop(receiver)) {
+    HInstruction* compare = flag->GetNext();
+    DCHECK(compare->IsNotEqual());
+    HInstruction* deopt = compare->GetNext();
+    DCHECK(deopt->IsDeoptimize());
+
+    // Advance instruction iterator first before we move the guard.
+    // We need to do it twice since we move three instructions and the
+    // visitor is responsible for advancing it once.
+    instruction_iterator_->Advance();
+    instruction_iterator_->Advance();
+
+    HBasicBlock* pre_header = loop_info->GetPreHeader();
+    flag->MoveBefore(pre_header->GetLastInstruction());
+    compare->MoveBefore(pre_header->GetLastInstruction());
+
+    block->RemoveInstruction(deopt);
+    HInstruction* suspend = loop_info->GetSuspendCheck();
+    // Need a new deoptimize instruction that copies the environment
+    // of the suspend instruction for the loop.
+    HDeoptimize* deoptimize =
+        new (GetGraph()->GetArena()) HDeoptimize(compare, suspend->GetDexPc());
+    pre_header->InsertInstructionBefore(deoptimize, pre_header->GetLastInstruction());
+    deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment(
+        suspend->GetEnvironment(), loop_info->GetHeader());
+    block_has_cha_guard_[pre_header->GetBlockId()] = 1;
+    GetGraph()->IncrementNumberOfCHAGuards();
+    return true;
+  }
+  return false;
+}
+
+void CHAGuardVisitor::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+  number_of_guards_to_visit_--;
+  HInstruction* receiver = flag->InputAt(0);
+  // Don't need the receiver anymore.
+  flag->RemoveInputAt(0);
+  if (receiver->IsNullCheck()) {
+    receiver = receiver->InputAt(0);
+  }
+
+  if (OptimizeForParameter(flag, receiver)) {
+    DCHECK(!flag->IsInBlock());
+    return;
+  }
+  if (OptimizeWithDominatingGuard(flag, receiver)) {
+    DCHECK(!flag->IsInBlock());
+    return;
+  }
+  if (HoistGuard(flag, receiver)) {
+    DCHECK(flag->IsInBlock());
+    return;
+  }
+
+  // Need to keep the CHA guard in place.
+  block_has_cha_guard_[flag->GetBlock()->GetBlockId()] = 1;
+  GetGraph()->IncrementNumberOfCHAGuards();
+}
+
+void CHAGuardOptimization::Run() {
+  if (graph_->GetNumberOfCHAGuards() == 0) {
+    return;
+  }
+  CHAGuardVisitor visitor(graph_);
+  for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+    visitor.VisitBasicBlock(block);
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h
new file mode 100644
index 0000000..ba0cdb8
--- /dev/null
+++ b/compiler/optimizing/cha_guard_optimization.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
+#define ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
+
+#include "optimization.h"
+
+namespace art {
+
+/**
+ * Optimize CHA guards by removing/moving them.
+ */
+class CHAGuardOptimization : public HOptimization {
+ public:
+  explicit CHAGuardOptimization(HGraph* graph)
+      : HOptimization(graph, kCHAGuardOptimizationPassName) {}
+
+  void Run() OVERRIDE;
+
+  static constexpr const char* kCHAGuardOptimizationPassName = "cha_guard_optimization";
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CHAGuardOptimization);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 1c5aec0..1df00ff 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -1243,10 +1243,26 @@
       move_resolver_(graph->GetArena(), this),
       assembler_(graph->GetArena()),
       isa_features_(isa_features),
+      uint32_literals_(std::less<uint32_t>(),
+                       graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      method_patches_(MethodReferenceComparator(),
+                      graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      call_patches_(MethodReferenceComparator(),
+                    graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      boot_image_string_patches_(StringReferenceValueComparator(),
+                                 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+      boot_image_type_patches_(TypeReferenceValueComparator(),
+                               graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      boot_image_address_patches_(std::less<uint32_t>(),
+                                  graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      jit_string_patches_(StringReferenceValueComparator(),
+                          graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      jit_class_patches_(TypeReferenceValueComparator(),
+                         graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
   // Always save the LR register to mimic Quick.
   AddAllocatedRegister(Location::RegisterLocation(LR));
   // Give d14 and d15 as scratch registers to VIXL.
@@ -4415,7 +4431,7 @@
     locations->AddTemp(Location::RequiresRegister());
   } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
     // We need a temporary register for the read barrier marking slow
-    // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier.
+    // path in CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier.
     locations->AddTemp(Location::RequiresRegister());
   }
 }
@@ -4877,7 +4893,7 @@
         object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
   }
   // We need a temporary register for the read barrier marking slow
-  // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
+  // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier.
   // Also need for String compression feature.
   if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
       || (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
@@ -5772,17 +5788,15 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
-      return HLoadClass::LoadKind::kDexCacheViaMethod;
+      DCHECK(!GetCompilerOptions().GetCompilePic());
+      break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
       DCHECK(GetCompilerOptions().GetCompilePic());
       break;
     case HLoadClass::LoadKind::kBootImageAddress:
-      // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
-      return HLoadClass::LoadKind::kDexCacheViaMethod;
+      break;
     case HLoadClass::LoadKind::kJitTableAddress:
-      // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
-      return HLoadClass::LoadKind::kDexCacheViaMethod;
+      break;
     case HLoadClass::LoadKind::kDexCachePcRelative:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       // We disable pc-relative load when there is an irreducible loop, as the optimization
@@ -5858,7 +5872,9 @@
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
-      TODO_VIXL32(FATAL);
+      DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
+      __ Ldr(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
+                                                            cls->GetTypeIndex()));
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
@@ -5869,11 +5885,18 @@
       break;
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
-      TODO_VIXL32(FATAL);
+      DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
+      DCHECK_NE(cls->GetAddress(), 0u);
+      uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+      __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
     case HLoadClass::LoadKind::kJitTableAddress: {
-      TODO_VIXL32(FATAL);
+      __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
+                                                       cls->GetTypeIndex(),
+                                                       cls->GetAddress()));
+      // /* GcRoot<mirror::Class> */ out = *out
+      GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
       break;
     }
     case HLoadClass::LoadKind::kDexCachePcRelative: {
@@ -5958,21 +5981,19 @@
     HLoadString::LoadKind desired_string_load_kind) {
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress:
-      // TODO(VIXL): Implement missing optimization.
-      return HLoadString::LoadKind::kDexCacheViaMethod;
+      DCHECK(!GetCompilerOptions().GetCompilePic());
+      break;
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
       DCHECK(GetCompilerOptions().GetCompilePic());
       break;
     case HLoadString::LoadKind::kBootImageAddress:
-      // TODO(VIXL): Implement missing optimization.
-      return HLoadString::LoadKind::kDexCacheViaMethod;
+      break;
     case HLoadString::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
     case HLoadString::LoadKind::kJitTableAddress:
       DCHECK(Runtime::Current()->UseJitCompilation());
-      // TODO(VIXL): Implement missing optimization.
-      return HLoadString::LoadKind::kDexCacheViaMethod;
+      break;
     case HLoadString::LoadKind::kDexCacheViaMethod:
       break;
   }
@@ -6014,8 +6035,9 @@
 
   switch (load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
-      TODO_VIXL32(FATAL);
-      break;
+      __ Ldr(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
+                                                              load->GetStringIndex()));
+      return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
       DCHECK(codegen_->GetCompilerOptions().IsBootImage());
@@ -6025,8 +6047,10 @@
       return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageAddress: {
-      TODO_VIXL32(FATAL);
-      break;
+      DCHECK_NE(load->GetAddress(), 0u);
+      uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+      __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
+      return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBssEntry: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
@@ -6043,8 +6067,11 @@
       return;
     }
     case HLoadString::LoadKind::kJitTableAddress: {
-      TODO_VIXL32(FATAL);
-      break;
+      __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
+                                                        load->GetStringIndex()));
+      // /* GcRoot<mirror::String> */ out = *out
+      GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
+      return;
     }
     default:
       break;
@@ -7207,19 +7234,6 @@
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
     const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
     HInvokeStaticOrDirect* invoke) {
-  // TODO(VIXL): Implement optimized code paths.
-  if (desired_dispatch_info.method_load_kind ==
-          HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup ||
-      desired_dispatch_info.code_ptr_location ==
-          HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup) {
-    return {
-      HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
-      HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-      0u,
-      0u
-    };
-  }
-
   HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
   // We disable pc-relative load when there is an irreducible loop, as the optimization
   // is incompatible with it.
@@ -7284,7 +7298,7 @@
   switch (invoke->GetCodePtrLocation()) {
     case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
       // LR = code address from literal pool with link-time patch.
-      TODO_VIXL32(FATAL);
+      __ Ldr(lr, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod()));
       break;
     case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
       // LR = invoke->GetDirectCodePtr();
@@ -7310,7 +7324,7 @@
       __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
       break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
-      TODO_VIXL32(FATAL);
+      __ Ldr(RegisterFrom(temp), DeduplicateMethodAddressLiteral(invoke->GetTargetMethod()));
       break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       HArmDexCacheArraysBase* base =
@@ -7464,6 +7478,57 @@
   return &patches->back();
 }
 
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageStringLiteral(
+    const DexFile& dex_file,
+    dex::StringIndex string_index) {
+  return boot_image_string_patches_.GetOrCreate(
+      StringReference(&dex_file, string_index),
+      [this]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+      });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageTypeLiteral(
+    const DexFile& dex_file,
+    dex::TypeIndex type_index) {
+  return boot_image_type_patches_.GetOrCreate(
+      TypeReference(&dex_file, type_index),
+      [this]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+      });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
+  bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
+  Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
+  return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateDexCacheAddressLiteral(uint32_t address) {
+  return DeduplicateUint32Literal(address, &uint32_literals_);
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(const DexFile& dex_file,
+                                                       dex::StringIndex string_index) {
+  jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u);
+  return jit_string_patches_.GetOrCreate(
+      StringReference(&dex_file, string_index),
+      [this]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+      });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
+                                                      dex::TypeIndex type_index,
+                                                      uint64_t address) {
+  jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address);
+  return jit_class_patches_.GetOrCreate(
+      TypeReference(&dex_file, type_index),
+      [this]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+      });
+}
+
 template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
 inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
     const ArenaDeque<PcRelativePatchInfo>& infos,
@@ -7487,11 +7552,34 @@
 void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
+      method_patches_.size() +
+      call_patches_.size() +
       relative_call_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() +
+      boot_image_string_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
-      /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size();
+      boot_image_type_patches_.size() +
+      /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() +
+      boot_image_address_patches_.size();
   linker_patches->reserve(size);
+  for (const auto& entry : method_patches_) {
+    const MethodReference& target_method = entry.first;
+    VIXLUInt32Literal* literal = entry.second;
+    DCHECK(literal->IsBound());
+    uint32_t literal_offset = literal->GetLocation();
+    linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset,
+                                                       target_method.dex_file,
+                                                       target_method.dex_method_index));
+  }
+  for (const auto& entry : call_patches_) {
+    const MethodReference& target_method = entry.first;
+    VIXLUInt32Literal* literal = entry.second;
+    DCHECK(literal->IsBound());
+    uint32_t literal_offset = literal->GetLocation();
+    linker_patches->push_back(LinkerPatch::CodePatch(literal_offset,
+                                                     target_method.dex_file,
+                                                     target_method.dex_method_index));
+  }
   for (const PatchInfo<vixl32::Label>& info : relative_call_patches_) {
     uint32_t literal_offset = info.label.GetLocation();
     linker_patches->push_back(
@@ -7499,6 +7587,15 @@
   }
   EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
                                                                linker_patches);
+  for (const auto& entry : boot_image_string_patches_) {
+    const StringReference& target_string = entry.first;
+    VIXLUInt32Literal* literal = entry.second;
+    DCHECK(literal->IsBound());
+    uint32_t literal_offset = literal->GetLocation();
+    linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
+                                                       target_string.dex_file,
+                                                       target_string.string_index.index_));
+  }
   if (!GetCompilerOptions().IsBootImage()) {
     EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
                                                                   linker_patches);
@@ -7506,8 +7603,54 @@
     EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
                                                                   linker_patches);
   }
+  for (const auto& entry : boot_image_type_patches_) {
+    const TypeReference& target_type = entry.first;
+    VIXLUInt32Literal* literal = entry.second;
+    DCHECK(literal->IsBound());
+    uint32_t literal_offset = literal->GetLocation();
+    linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
+                                                     target_type.dex_file,
+                                                     target_type.type_index.index_));
+  }
   EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
                                                               linker_patches);
+  for (const auto& entry : boot_image_address_patches_) {
+    DCHECK(GetCompilerOptions().GetIncludePatchInformation());
+    VIXLUInt32Literal* literal = entry.second;
+    DCHECK(literal->IsBound());
+    uint32_t literal_offset = literal->GetLocation();
+    linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+  }
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
+    uint32_t value,
+    Uint32ToLiteralMap* map) {
+  return map->GetOrCreate(
+      value,
+      [this, value]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ value);
+      });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateMethodLiteral(
+    MethodReference target_method,
+    MethodToLiteralMap* map) {
+  return map->GetOrCreate(
+      target_method,
+      [this]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+      });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateMethodAddressLiteral(
+    MethodReference target_method) {
+  return DeduplicateMethodLiteral(target_method, &method_patches_);
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateMethodCodeLiteral(
+    MethodReference target_method) {
+  return DeduplicateMethodLiteral(target_method, &call_patches_);
 }
 
 void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
@@ -7703,6 +7846,31 @@
   }
 }
 
+static void PatchJitRootUse(uint8_t* code,
+                            const uint8_t* roots_data,
+                            VIXLUInt32Literal* literal,
+                            uint64_t index_in_table) {
+  DCHECK(literal->IsBound());
+  uint32_t literal_offset = literal->GetLocation();
+  uintptr_t address =
+      reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
+  uint8_t* data = code + literal_offset;
+  reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
+}
+
+void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
+  for (const auto& entry : jit_string_patches_) {
+    const auto& it = jit_string_roots_.find(entry.first);
+    DCHECK(it != jit_string_roots_.end());
+    PatchJitRootUse(code, roots_data, entry.second, it->second);
+  }
+  for (const auto& entry : jit_class_patches_) {
+    const auto& it = jit_class_roots_.find(entry.first);
+    DCHECK(it != jit_class_roots_.end());
+    PatchJitRootUse(code, roots_data, entry.second, it->second);
+  }
+}
+
 void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
     CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
     vixl32::Register out) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 93ea601..45bd164 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -107,20 +107,20 @@
     arraysize(kRuntimeParameterFpuRegistersVIXL);
 
 class LoadClassSlowPathARMVIXL;
-
 class CodeGeneratorARMVIXL;
 
+using VIXLInt32Literal = vixl::aarch32::Literal<int32_t>;
+using VIXLUInt32Literal = vixl::aarch32::Literal<uint32_t>;
+
 class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> {
  public:
-  typedef vixl::aarch32::Literal<int32_t> IntLiteral;
-
   explicit JumpTableARMVIXL(HPackedSwitch* switch_instr)
       : switch_instr_(switch_instr),
         table_start_(),
         bb_addresses_(switch_instr->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
     uint32_t num_entries = switch_instr_->GetNumEntries();
     for (uint32_t i = 0; i < num_entries; i++) {
-      IntLiteral *lit = new IntLiteral(0, vixl32::RawLiteral::kManuallyPlaced);
+      VIXLInt32Literal *lit = new VIXLInt32Literal(0, vixl32::RawLiteral::kManuallyPlaced);
       bb_addresses_.emplace_back(lit);
     }
   }
@@ -133,7 +133,7 @@
  private:
   HPackedSwitch* const switch_instr_;
   vixl::aarch32::Label table_start_;
-  ArenaVector<std::unique_ptr<IntLiteral>> bb_addresses_;
+  ArenaVector<std::unique_ptr<VIXLInt32Literal>> bb_addresses_;
 
   DISALLOW_COPY_AND_ASSIGN(JumpTableARMVIXL);
 };
@@ -566,8 +566,22 @@
   PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
   PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
                                                        uint32_t element_offset);
+  VIXLUInt32Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+                                                       dex::StringIndex string_index);
+  VIXLUInt32Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
+                                                     dex::TypeIndex type_index);
+  VIXLUInt32Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
+  VIXLUInt32Literal* DeduplicateDexCacheAddressLiteral(uint32_t address);
+  VIXLUInt32Literal* DeduplicateJitStringLiteral(const DexFile& dex_file,
+                                                 dex::StringIndex string_index);
+  VIXLUInt32Literal* DeduplicateJitClassLiteral(const DexFile& dex_file,
+                                                dex::TypeIndex type_index,
+                                                uint64_t address);
+
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
+  void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE;
+
   // Fast path implementation of ReadBarrier::Barrier for a heap
   // reference field load when Baker's read barriers are used.
   void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -673,10 +687,21 @@
   vixl::aarch32::Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
                                                                 vixl::aarch32::Register temp);
 
-  using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::aarch32::Literal<uint32_t>*>;
+  using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, VIXLUInt32Literal*>;
   using MethodToLiteralMap =
-      ArenaSafeMap<MethodReference, vixl::aarch32::Literal<uint32_t>*, MethodReferenceComparator>;
+      ArenaSafeMap<MethodReference, VIXLUInt32Literal*, MethodReferenceComparator>;
+  using StringToLiteralMap = ArenaSafeMap<StringReference,
+                                          VIXLUInt32Literal*,
+                                          StringReferenceValueComparator>;
+  using TypeToLiteralMap = ArenaSafeMap<TypeReference,
+                                        VIXLUInt32Literal*,
+                                        TypeReferenceValueComparator>;
 
+  VIXLUInt32Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
+  VIXLUInt32Literal* DeduplicateMethodLiteral(MethodReference target_method,
+                                              MethodToLiteralMap* map);
+  VIXLUInt32Literal* DeduplicateMethodAddressLiteral(MethodReference target_method);
+  VIXLUInt32Literal* DeduplicateMethodCodeLiteral(MethodReference target_method);
   PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
                                           uint32_t offset_or_index,
                                           ArenaDeque<PcRelativePatchInfo>* patches);
@@ -697,15 +722,31 @@
   ArmVIXLAssembler assembler_;
   const ArmInstructionSetFeatures& isa_features_;
 
+  // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
+  Uint32ToLiteralMap uint32_literals_;
+  // Method patch info, map MethodReference to a literal for method address and method code.
+  MethodToLiteralMap method_patches_;
+  MethodToLiteralMap call_patches_;
   // Relative call patch info.
   // Using ArenaDeque<> which retains element addresses on push/emplace_back().
   ArenaDeque<PatchInfo<vixl::aarch32::Label>> relative_call_patches_;
   // PC-relative patch info for each HArmDexCacheArraysBase.
   ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+  // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
+  StringToLiteralMap boot_image_string_patches_;
   // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
   ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+  // Deduplication map for boot type literals for kBootImageLinkTimeAddress.
+  TypeToLiteralMap boot_image_type_patches_;
   // PC-relative type patch info.
   ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
+  // Deduplication map for patchable boot image addresses.
+  Uint32ToLiteralMap boot_image_address_patches_;
+
+  // Patches for string literals in JIT compiled code.
+  StringToLiteralMap jit_string_patches_;
+  // Patches for class literals in JIT compiled code.
+  TypeToLiteralMap jit_class_patches_;
 
   DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL);
 };
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index b21bc09..5456b1e 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -73,10 +73,18 @@
 }
 
 /**
- * Returns narrowest data type.
+ * Returns result of implicit widening type conversion done in HIR.
  */
-static Primitive::Type Narrowest(Primitive::Type type1, Primitive::Type type2) {
-  return Primitive::ComponentSize(type1) <= Primitive::ComponentSize(type2) ? type1 : type2;
+static Primitive::Type ImplicitConversion(Primitive::Type type) {
+  switch (type) {
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimBoolean:
+      return Primitive::kPrimInt;
+    default:
+      return type;
+  }
 }
 
 //
@@ -232,9 +240,9 @@
   } else if (instruction->IsSelect()) {
     info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 1);
   } else if (instruction->IsTypeConversion()) {
-    info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)),
-                       instruction->AsTypeConversion()->GetInputType(),
-                       instruction->AsTypeConversion()->GetResultType());
+    info = TransferConversion(LookupInfo(loop, instruction->InputAt(0)),
+                              instruction->AsTypeConversion()->GetInputType(),
+                              instruction->AsTypeConversion()->GetResultType());
   } else if (instruction->IsBoundsCheck()) {
     info = LookupInfo(loop, instruction->InputAt(0));  // Pass-through.
   }
@@ -267,8 +275,12 @@
     return;
   }
 
-  // Store interesting cycle.
-  AssignCycle(phi->AsPhi());
+  // Store interesting cycle in each loop phi.
+  for (size_t i = 0; i < size; i++) {
+    if (scc_[i]->IsLoopHeaderPhi()) {
+      AssignCycle(scc_[i]->AsPhi());
+    }
+  }
 
   // Singleton is wrap-around induction if all internal links have the same meaning.
   if (size == 1) {
@@ -326,7 +338,7 @@
     } else if (instruction->IsSelect()) {
       update = SolvePhi(instruction, /*input_index*/ 0, /*adjust_input_size*/ 1);  // acts like Phi
     } else if (instruction->IsTypeConversion()) {
-      update = SolveCnv(instruction->AsTypeConversion());
+      update = SolveConversion(loop, phi, instruction->AsTypeConversion());
     }
     if (update == nullptr) {
       return;
@@ -416,8 +428,9 @@
   // wrap-around, or periodic can be combined with an invariant to yield a similar result.
   // Two linear or two polynomial inputs can be combined too. Other combinations fail.
   if (a != nullptr && b != nullptr) {
-    type_ = Narrowest(type_, Narrowest(a->type, b->type));
-    if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
+    if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
+      return nullptr;  // no transfer
+    } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
       return CreateInvariantOp(op, a, b);
     } else if ((a->induction_class == kLinear && b->induction_class == kLinear) ||
                (a->induction_class == kPolynomial && b->induction_class == kPolynomial)) {
@@ -452,8 +465,9 @@
   // Transfer over a unary negation: an invariant, linear, polynomial, geometric (mul),
   // wrap-around, or periodic input yields a similar but negated induction as result.
   if (a != nullptr) {
-    type_ = Narrowest(type_, a->type);
-    if (a->induction_class == kInvariant) {
+    if (IsNarrowingLinear(a)) {
+      return nullptr;  // no transfer
+    } else if (a->induction_class == kInvariant) {
       return CreateInvariantOp(kNeg, nullptr, a);
     } else if (a->induction_class != kGeometric || a->operation == kMul) {
       return CreateInduction(a->induction_class,
@@ -473,8 +487,9 @@
   // wrap-around, or periodic can be multiplied with an invariant to yield a similar
   // but multiplied result. Two non-invariant inputs cannot be multiplied, however.
   if (a != nullptr && b != nullptr) {
-    type_ = Narrowest(type_, Narrowest(a->type, b->type));
-    if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
+    if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
+      return nullptr;  // no transfer
+    } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
       return CreateInvariantOp(kMul, a, b);
     } else if (a->induction_class == kInvariant && (b->induction_class != kGeometric ||
                                                     b->operation == kMul)) {
@@ -497,17 +512,17 @@
   return nullptr;
 }
 
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a,
-                                                                         Primitive::Type from,
-                                                                         Primitive::Type to) {
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferConversion(
+    InductionInfo* a,
+    Primitive::Type from,
+    Primitive::Type to) {
   if (a != nullptr) {
-    // Allow narrowing conversion on linear induction in certain cases.
-    if (IsNarrowingIntegralConversion(from, to)) {
-      if (a->induction_class == kLinear) {
-        if (a->type == to || (a->type == from && IsNarrowingIntegralConversion(from, to))) {
-          return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to);
-        }
-      }
+    // Allow narrowing conversion on linear induction in certain cases:
+    // induction is already at narrow type, or can be made narrower.
+    if (IsNarrowingIntegralConversion(from, to) &&
+        a->induction_class == kLinear &&
+        (a->type == to || IsNarrowingIntegralConversion(a->type, to))) {
+      return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to);
     }
   }
   return nullptr;
@@ -700,16 +715,29 @@
   return nullptr;
 }
 
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) {
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveConversion(
+    HLoopInformation* loop,
+    HInstruction* entry_phi,
+    HTypeConversion* conversion) {
   Primitive::Type from = conversion->GetInputType();
   Primitive::Type to = conversion->GetResultType();
-  // A narrowing conversion is allowed within the cycle of a linear induction, provided that the
-  // narrowest encountered type is recorded with the induction to account for the precision loss.
-  if (IsNarrowingIntegralConversion(from, to)) {
-    auto it = cycle_.find(conversion->GetInput());
-    if (it != cycle_.end() && it->second->induction_class == kInvariant) {
-      type_ = Narrowest(type_, to);
-      return it->second;
+  // A narrowing conversion is allowed as *last* operation of the cycle of a linear induction
+  // with an initial value that fits the type, provided that the narrowest encountered type is
+  // recorded with the induction to account for the precision loss. The narrower induction does
+  // *not* transfer to any wider operations, however, since these may yield out-of-type values
+  if (entry_phi->InputCount() == 2 && conversion == entry_phi->InputAt(1)) {
+    int64_t min = Primitive::MinValueOfIntegralType(to);
+    int64_t max = Primitive::MaxValueOfIntegralType(to);
+    int64_t value = 0;
+    InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
+    if (IsNarrowingIntegralConversion(from, to) &&
+        IsAtLeast(initial, &value) && value >= min &&
+        IsAtMost(initial, &value)  && value <= max) {
+      auto it = cycle_.find(conversion->GetInput());
+      if (it != cycle_.end() && it->second->induction_class == kInvariant) {
+        type_ = to;
+        return it->second;
+      }
     }
   }
   return nullptr;
@@ -729,7 +757,7 @@
       HCondition* condition = if_expr->AsCondition();
       InductionInfo* a = LookupInfo(loop, condition->InputAt(0));
       InductionInfo* b = LookupInfo(loop, condition->InputAt(1));
-      Primitive::Type type = condition->InputAt(0)->GetType();
+      Primitive::Type type = ImplicitConversion(condition->InputAt(0)->GetType());
       // Determine if the loop control uses a known sequence on an if-exit (X outside) or on
       // an if-iterate (X inside), expressed as if-iterate when passed into VisitCondition().
       if (a == nullptr || b == nullptr) {
@@ -901,8 +929,8 @@
                                      int64_t stride_value,
                                      Primitive::Type type,
                                      IfCondition cmp) {
-  const int64_t min = Primitive::MinValueOfIntegralType(type);
-  const int64_t max = Primitive::MaxValueOfIntegralType(type);
+  int64_t min = Primitive::MinValueOfIntegralType(type);
+  int64_t max = Primitive::MaxValueOfIntegralType(type);
   // Some rules under which it is certain at compile-time that the loop is finite.
   int64_t value;
   switch (cmp) {
@@ -938,8 +966,6 @@
     min++;
   }
   // Do both bounds fit the range?
-  // Note: The `value` is initialized to please valgrind - the compiler can reorder
-  // the return value check with the `value` check, b/27651442 .
   int64_t value = 0;
   return IsAtLeast(lower_expr, &value) && value >= min &&
          IsAtMost(lower_expr, &value)  && value <= max &&
@@ -1046,7 +1072,8 @@
       return CreateSimplifiedInvariant(kSub, b->op_b, b->op_a);
     }
   }
-  return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type);
+  return new (graph_->GetArena()) InductionInfo(
+      kInvariant, op, a, b, nullptr, ImplicitConversion(b->type));
 }
 
 HInstruction* HInductionVarAnalysis::GetShiftConstant(HLoopInformation* loop,
@@ -1108,6 +1135,16 @@
   return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value);
 }
 
+bool HInductionVarAnalysis::IsNarrowingLinear(InductionInfo* info) {
+  return info != nullptr &&
+      info->induction_class == kLinear &&
+      (info->type == Primitive::kPrimByte ||
+       info->type == Primitive::kPrimShort ||
+       info->type == Primitive::kPrimChar ||
+       (info->type == Primitive::kPrimInt && (info->op_a->type == Primitive::kPrimLong ||
+                                              info->op_b->type == Primitive::kPrimLong)));
+}
+
 bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
                                            InductionInfo* info2) {
   // Test structural equality only, without accounting for simplifications.
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 293aa70..39b39cd 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -167,7 +167,7 @@
   InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op);
   InductionInfo* TransferNeg(InductionInfo* a);
   InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
-  InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to);
+  InductionInfo* TransferConversion(InductionInfo* a, Primitive::Type from, Primitive::Type to);
 
   // Solvers.
   InductionInfo* SolvePhi(HInstruction* phi, size_t input_index, size_t adjust_input_size);
@@ -191,7 +191,9 @@
                            HInstruction* entry_phi,
                            HInstruction* instruction,
                            int64_t oppositive_value);
-  InductionInfo* SolveCnv(HTypeConversion* conversion);
+  InductionInfo* SolveConversion(HLoopInformation* loop,
+                                 HInstruction* entry_phi,
+                                 HTypeConversion* conversion);
 
   // Trip count information.
   void VisitControl(HLoopInformation* loop);
@@ -235,6 +237,7 @@
   bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value);
 
   // Helpers.
+  static bool IsNarrowingLinear(InductionInfo* info);
   static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
   static std::string FetchToString(HInstruction* fetch);
   static std::string InductionToString(InductionInfo* info);
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index f52a1aa..82ee93d 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -174,6 +174,12 @@
       iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2));
   }
 
+  // Returns true for narrowing linear induction.
+  bool IsNarrowingLinear(HInstruction* instruction) {
+    return HInductionVarAnalysis::IsNarrowingLinear(
+        iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction));
+  }
+
   // Performs InductionVarAnalysis (after proper set up).
   void PerformInductionVarAnalysis() {
     graph_->BuildDominatorTree();
@@ -1066,16 +1072,20 @@
   // }
   BuildLoopNest(1);
   HInstruction* conv = InsertInstruction(
-      new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
   HInstruction* store1 = InsertArrayStore(conv, 0);
   HInstruction* store2 = InsertArrayStore(basic_[0], 0);
   PerformInductionVarAnalysis();
 
-  // Regular int induction (i) is "transferred" over conversion into byte induction (k).
+  // Regular int induction (i) is transferred over conversion into byte induction (k).
   EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
   EXPECT_STREQ("((1) * i + (0)):PrimInt",  GetInductionInfo(store2->InputAt(1), 0).c_str());
   EXPECT_STREQ("((1) * i + (1)):PrimInt",  GetInductionInfo(increment_[0], 0).c_str());
 
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
+  EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1)));
+
   // Type matters!
   EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
 
@@ -1093,7 +1103,7 @@
   // }
   BuildLoopNest(1);
   HInstruction* conv = InsertInstruction(
-      new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
   HInstruction* store1 = InsertArrayStore(conv, 0);
   HInstruction* add = InsertInstruction(
       new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
@@ -1101,11 +1111,86 @@
 
   PerformInductionVarAnalysis();
 
-  // Byte induction (k) is "transferred" over conversion into addition (k + 1).
-  // This means only values within byte range can be trusted (even though
-  // addition can jump out of the range of course).
+  // Byte induction (k) is detected, but it does not transfer over the addition,
+  // since this may yield out-of-type values.
   EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
-  EXPECT_STREQ("((1) * i + (1)):PrimByte", GetInductionInfo(store2->InputAt(1), 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(store2->InputAt(1), 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
+  EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1)));  // works for null
+}
+
+TEST_F(InductionVarAnalysisTest, ByteInduction) {
+  // Setup:
+  // k = -128;
+  // for (int i = 0; i < 100; i++) {
+  //   k = k + 1;
+  //   k = (byte) k;
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(graph_->GetIntConstant(-128));
+
+  HInstruction* add = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+  HInstruction* conv = InsertInstruction(
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0);
+  k_header->AddInput(conv);
+  PerformInductionVarAnalysis();
+
+  // Byte induction (k) is detected, but it does not transfer over the addition,
+  // since this may yield out-of-type values.
+  EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(k_header));
+  EXPECT_FALSE(IsNarrowingLinear(add));  // works for null
+}
+
+TEST_F(InductionVarAnalysisTest, NoByteInduction1) {
+  // Setup:
+  // k = -129;  / does not fit!
+  // for (int i = 0; i < 100; i++) {
+  //   k = k + 1;
+  //   k = (byte) k;
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(graph_->GetIntConstant(-129));
+
+  HInstruction* add = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+  HInstruction* conv = InsertInstruction(
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0);
+  k_header->AddInput(conv);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, NoByteInduction2) {
+  // Setup:
+  // k = 0;
+  // for (int i = 0; i < 100; i++) {
+  //   k = (byte) k;   // conversion not done last!
+  //   k = k + 1;
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
+
+  HInstruction* conv = InsertInstruction(
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, k_header, kNoDexPc), 0);
+  HInstruction* add = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
+  k_header->AddInput(add);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
@@ -1116,12 +1201,20 @@
   basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count.
   EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", GetTripCount(0).c_str());
 }
@@ -1134,12 +1227,20 @@
   basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count undefined.
   EXPECT_STREQ("", GetTripCount(0).c_str());
 }
@@ -1152,13 +1253,20 @@
   basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
-               GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count.
   EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", GetTripCount(0).c_str());
 }
@@ -1171,13 +1279,20 @@
   basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
-               GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count undefined.
   EXPECT_STREQ("", GetTripCount(0).c_str());
 }
@@ -1189,12 +1304,20 @@
   BuildLoopNest(1);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count.
   EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", GetTripCount(0).c_str());
 }
@@ -1206,12 +1329,20 @@
   BuildLoopNest(1);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count undefined.
   EXPECT_STREQ("", GetTripCount(0).c_str());
 }
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 7bcc384..d5c4c2f 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -169,8 +169,8 @@
     case Primitive::kPrimByte: {
       // Constants within range only.
       // TODO: maybe some room for improvement, like allowing widening conversions
-      const int32_t min = Primitive::MinValueOfIntegralType(type);
-      const int32_t max = Primitive::MaxValueOfIntegralType(type);
+      int32_t min = Primitive::MinValueOfIntegralType(type);
+      int32_t max = Primitive::MaxValueOfIntegralType(type);
       return (IsConstantValue(v) && min <= v.b_constant && v.b_constant <= max)
           ? v
           : InductionVarRange::Value();
@@ -551,7 +551,7 @@
   int64_t b = 0;
   if (IsConstant(info->op_a->op_a, kExact, &a) && CanLongValueFitIntoInt(a) && a >= 0 &&
       IsConstant(info->op_a->op_b, kExact, &b) && CanLongValueFitIntoInt(b) && b >= 0) {
-    // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known
+    // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for
     // maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
     Value c = GetVal(info->op_b, trip, in_body, is_min);
     if (is_min) {
@@ -629,6 +629,7 @@
     }
   } else if (instruction->IsTypeConversion()) {
     // Since analysis is 32-bit (or narrower), chase beyond widening along the path.
+    // For example, this discovers the length in: for (long i = 0; i < a.length; i++);
     if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt &&
         instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) {
       return GetFetch(instruction->InputAt(0), trip, in_body, is_min);
@@ -843,7 +844,7 @@
 
 InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
   if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) {
-    const int32_t b = v1.b_constant + v2.b_constant;
+    int32_t b = v1.b_constant + v2.b_constant;
     if (v1.a_constant == 0) {
       return Value(v2.instruction, v2.a_constant, b);
     } else if (v2.a_constant == 0) {
@@ -857,7 +858,7 @@
 
 InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const {
   if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) {
-    const int32_t b = v1.b_constant - v2.b_constant;
+    int32_t b = v1.b_constant - v2.b_constant;
     if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
       return Value(v2.instruction, -v2.a_constant, b);
     } else if (v2.a_constant == 0) {
@@ -988,13 +989,16 @@
       IsConstant(trip->op_a, kExact, &m) && m >= 1) {
     // Evaluate bounds on sum_i=0^m-1(a * i + b) + c for known
     // maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
-    // TODO: generalize
-    HInstruction* c_instr = nullptr;
-    if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c_instr : nullptr, false, false)) {
+    HInstruction* c = nullptr;
+    if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c : nullptr, false, false)) {
       if (graph != nullptr) {
+        Primitive::Type type = info->type;
         int64_t sum = a * ((m * (m - 1)) / 2) + b * m;
-        *result = Insert(block, new (graph->GetArena()) HAdd(info->type,
-                                                             graph->GetIntConstant(sum), c_instr));
+        if (type != Primitive::kPrimLong) {
+          sum = static_cast<int32_t>(sum);  // okay to truncate
+        }
+        *result =
+            Insert(block, new (graph->GetArena()) HAdd(type, graph->GetConstant(type, sum), c));
       }
       return true;
     }
@@ -1011,35 +1015,33 @@
   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric);
   // Detect known base and trip count (always taken).
   int64_t f = 0;
-  int64_t t = 0;
-  if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &t) && t >= 1) {
+  int64_t m = 0;
+  if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &m) && m >= 1) {
     HInstruction* opa = nullptr;
     HInstruction* opb = nullptr;
     if (GenerateCode(info->op_a, nullptr, graph, block, &opa, false, false) &&
         GenerateCode(info->op_b, nullptr, graph, block, &opb, false, false)) {
-      // Compute f ^ t.
-      int64_t fpowt = IntPow(f, t);
+      // Compute f ^ m for known maximum index value m.
+      int64_t fpow = IntPow(f, m);
       if (graph != nullptr) {
-        DCHECK(info->type == Primitive::kPrimInt);  // due to codegen, generalize?
-        if (fpowt == 0) {
+        DCHECK(info->operation == HInductionVarAnalysis::kMul ||
+               info->operation == HInductionVarAnalysis::kDiv);
+        Primitive::Type type = info->type;
+        if (fpow == 0) {
           // Special case: repeated mul/div always yields zero.
-          *result = graph->GetIntConstant(0);
-        } else if (info->operation == HInductionVarAnalysis::kMul) {
-          // Last value multiplication: a * f ^ t + b.
-          HInstruction* mul = Insert(block,
-                                     new (graph->GetArena()) HMul(info->type,
-                                                                  opa,
-                                                                  graph->GetIntConstant(fpowt)));
-          *result = Insert(block, new (graph->GetArena()) HAdd(info->type, mul, opb));
+          *result = graph->GetConstant(type, 0);
         } else {
-          // Last value multiplication: a * f ^ -t + b.
-          DCHECK_EQ(info->operation, HInductionVarAnalysis::kDiv);
-          HInstruction* div = Insert(block,
-                                     new (graph->GetArena()) HDiv(info->type,
-                                                                  opa,
-                                                                  graph->GetIntConstant(fpowt),
-                                                                  kNoDexPc));
-          *result = Insert(block, new (graph->GetArena()) HAdd(info->type, div, opb));
+          // Last value: a * f ^ m + b or a * f ^ -m + b.
+          if (type != Primitive::kPrimLong) {
+            fpow = static_cast<int32_t>(fpow);  // okay to truncate
+          }
+          HInstruction* e = nullptr;
+          if (info->operation == HInductionVarAnalysis::kMul) {
+            e = new (graph->GetArena()) HMul(type, opa, graph->GetConstant(type, fpow));
+          } else {
+            e = new (graph->GetArena()) HDiv(type, opa, graph->GetConstant(type, fpow), kNoDexPc);
+          }
+          *result = Insert(block, new (graph->GetArena()) HAdd(type, Insert(block, e), opb));
         }
       }
       return true;
@@ -1060,12 +1062,11 @@
   for (; info->induction_class == HInductionVarAnalysis::kWrapAround;
        info = info->op_b, ++depth) {}
   // Handle wrap(x, wrap(.., y)) if trip count reaches an invariant at end.
-  // TODO: generalize
-  int64_t t = 0;
+  // TODO: generalize, but be careful to adjust the terminal.
+  int64_t m = 0;
   if (info->induction_class == HInductionVarAnalysis::kInvariant &&
-      IsConstant(trip->op_a, kExact, &t) && t >= depth &&
-      GenerateCode(info, nullptr, graph, block, result, false, false)) {
-    return true;
+      IsConstant(trip->op_a, kExact, &m) && m >= depth) {
+    return GenerateCode(info, nullptr, graph, block, result, false, false);
   }
   return false;
 }
@@ -1079,43 +1080,49 @@
   DCHECK(info != nullptr);
   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPeriodic);
   // Count period.
-  int32_t period = 1;
+  int64_t period = 1;
   for (HInductionVarAnalysis::InductionInfo* p = info;
        p->induction_class == HInductionVarAnalysis::kPeriodic;
        p = p->op_b, ++period) {}
-  // Handle periodic(x, y) case for restricted types.
-  // TODO: generalize
-  if (period != 2 ||
-      trip->op_a->type != Primitive::kPrimInt ||
-      (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean)) {
-    return false;
+  // Handle any periodic(x, periodic(.., y)) for known maximum index value m.
+  int64_t m = 0;
+  if (IsConstant(trip->op_a, kExact, &m) && m >= 1) {
+    int64_t li = m % period;
+    for (int64_t i = 0; i < li; info = info->op_b, i++) {}
+    if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
+      info = info->op_a;
+    }
+    return GenerateCode(info, nullptr, graph, block, result, false, false);
   }
-  HInstruction* x_instr = nullptr;
-  HInstruction* y_instr = nullptr;
-  HInstruction* trip_expr = nullptr;
-  if (GenerateCode(info->op_a, nullptr, graph, block, graph ? &x_instr   : nullptr, false, false) &&
-      GenerateCode(info->op_b, nullptr, graph, block, graph ? &y_instr   : nullptr, false, false) &&
-      GenerateCode(trip->op_a, nullptr, graph, block, graph ? &trip_expr : nullptr, false, false)) {
-    // During actual code generation (graph != nullptr),
-    // generate is_even ? x : y select instruction.
+  // Handle periodic(x, y) using even/odd-select on trip count. Enter trip count expression
+  // directly to obtain the maximum index value t even if taken test is needed.
+  HInstruction* x = nullptr;
+  HInstruction* y = nullptr;
+  HInstruction* t = nullptr;
+  if (period == 2 &&
+      GenerateCode(info->op_a, nullptr, graph, block, graph ? &x : nullptr, false, false) &&
+      GenerateCode(info->op_b, nullptr, graph, block, graph ? &y : nullptr, false, false) &&
+      GenerateCode(trip->op_a, nullptr, graph, block, graph ? &t : nullptr, false, false)) {
+    // During actual code generation (graph != nullptr), generate is_even ? x : y.
     if (graph != nullptr) {
-      HInstruction* is_even = Insert(block, new (graph->GetArena()) HEqual(
-          Insert(block, new (graph->GetArena()) HAnd(
-              Primitive::kPrimInt, trip_expr, graph->GetIntConstant(1))),
-          graph->GetIntConstant(0), kNoDexPc));
-      *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x_instr, y_instr, kNoDexPc));
+      Primitive::Type type = trip->type;
+      HInstruction* msk =
+          Insert(block, new (graph->GetArena()) HAnd(type, t, graph->GetConstant(type, 1)));
+      HInstruction* is_even =
+          Insert(block, new (graph->GetArena()) HEqual(msk, graph->GetConstant(type, 0), kNoDexPc));
+      *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x, y, kNoDexPc));
     }
     // Guard select with taken test if needed.
     if (*needs_taken_test) {
-      HInstruction* taken_test = nullptr;
-      if (!GenerateCode(
-          trip->op_b, nullptr, graph, block, graph ? &taken_test : nullptr, false, false)) {
+      HInstruction* is_taken = nullptr;
+      if (GenerateCode(trip->op_b, nullptr, graph, block, graph ? &is_taken : nullptr, false, false)) {
+        if (graph != nullptr) {
+          *result = Insert(block, new (graph->GetArena()) HSelect(is_taken, *result, x, kNoDexPc));
+        }
+        *needs_taken_test = false;  // taken care of
+      } else {
         return false;
-      } else if (graph != nullptr) {
-         *result = Insert(block,
-                          new (graph->GetArena()) HSelect(taken_test, *result, x_instr, kNoDexPc));
       }
-      *needs_taken_test = false;  // taken care of
     }
     return true;
   }
@@ -1134,13 +1141,8 @@
     if (graph != nullptr && result == nullptr) {
       return true;
     }
-    // Verify type safety.
-    // TODO: generalize
-    Primitive::Type type = Primitive::kPrimInt;
-    if (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean) {
-      return false;
-    }
     // Handle current operation.
+    Primitive::Type type = info->type;
     HInstruction* opa = nullptr;
     HInstruction* opb = nullptr;
     switch (info->induction_class) {
@@ -1214,15 +1216,15 @@
           case HInductionVarAnalysis::kTripCountInBodyUnsafe:
             if (is_min) {
               if (graph != nullptr) {
-                *result = graph->GetIntConstant(0);
+                *result = graph->GetConstant(type, 0);
               }
               return true;
             } else if (in_body) {
               if (GenerateCode(info->op_a, trip, graph, block, &opb, in_body, is_min)) {
                 if (graph != nullptr) {
-                  *result = Insert(block,
-                                   new (graph->GetArena())
-                                       HSub(type, opb, graph->GetIntConstant(1)));
+                  *result =
+                      Insert(block,
+                             new (graph->GetArena()) HSub(type, opb, graph->GetConstant(type, 1)));
                 }
                 return true;
               }
@@ -1236,26 +1238,31 @@
         // Linear induction a * i + b, for normalized 0 <= i < TC. For ranges, this should
         // be restricted to a unit stride to avoid arithmetic wrap-around situations that
         // are harder to guard against. For a last value, requesting min/max based on any
-        // stride yields right value.
-        int64_t stride_value = 0;
-        if (IsConstant(info->op_a, kExact, &stride_value)) {
-          const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
-          if (GenerateCode(trip,       trip, graph, block, &opa, in_body, is_min_a) &&
-              GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
-            if (graph != nullptr) {
-              HInstruction* oper;
-              if (stride_value == 1) {
-                oper = new (graph->GetArena()) HAdd(type, opa, opb);
-              } else if (stride_value == -1) {
-                oper = new (graph->GetArena()) HSub(type, opb, opa);
-              } else {
-                HInstruction* mul = new (graph->GetArena()) HMul(
-                    type, graph->GetIntConstant(stride_value), opa);
-                oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb);
+        // known stride yields right value. Always avoid any narrowing linear induction or
+        // any type mismatch between the linear induction and the trip count expression.
+        // TODO: careful runtime type conversions could generalize this latter restriction.
+        if (!HInductionVarAnalysis::IsNarrowingLinear(info) && trip->type == type) {
+          int64_t stride_value = 0;
+          if (IsConstant(info->op_a, kExact, &stride_value) &&
+              CanLongValueFitIntoInt(stride_value)) {
+            const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
+            if (GenerateCode(trip,       trip, graph, block, &opa, in_body, is_min_a) &&
+                GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
+              if (graph != nullptr) {
+                HInstruction* oper;
+                if (stride_value == 1) {
+                  oper = new (graph->GetArena()) HAdd(type, opa, opb);
+                } else if (stride_value == -1) {
+                  oper = new (graph->GetArena()) HSub(type, opb, opa);
+                } else {
+                  HInstruction* mul =
+                      new (graph->GetArena()) HMul(type, graph->GetConstant(type, stride_value), opa);
+                  oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb);
+                }
+                *result = Insert(block, oper);
               }
-              *result = Insert(block, oper);
+              return true;
             }
-            return true;
           }
         }
         break;
@@ -1270,7 +1277,7 @@
         Value extreme = GetVal(info, trip, in_body, is_min);
         if (IsConstantValue(extreme)) {
           if (graph != nullptr) {
-            *result = graph->GetIntConstant(extreme.b_constant);
+            *result = graph->GetConstant(type, extreme.b_constant);
           }
           return true;
         }
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 11471af..d847879 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -504,19 +504,25 @@
                            uint32_t dex_pc,
                            HInstruction* cursor,
                            HBasicBlock* bb_cursor) {
-  HInstruction* deopt_flag = new (graph_->GetArena()) HShouldDeoptimizeFlag(dex_pc);
-  HInstruction* should_deopt = new (graph_->GetArena()) HNotEqual(
+  HShouldDeoptimizeFlag* deopt_flag = new (graph_->GetArena())
+      HShouldDeoptimizeFlag(graph_->GetArena(), dex_pc);
+  HInstruction* compare = new (graph_->GetArena()) HNotEqual(
       deopt_flag, graph_->GetIntConstant(0, dex_pc));
-  HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(should_deopt, dex_pc);
+  HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(compare, dex_pc);
 
   if (cursor != nullptr) {
     bb_cursor->InsertInstructionAfter(deopt_flag, cursor);
   } else {
     bb_cursor->InsertInstructionBefore(deopt_flag, bb_cursor->GetFirstInstruction());
   }
-  bb_cursor->InsertInstructionAfter(should_deopt, deopt_flag);
-  bb_cursor->InsertInstructionAfter(deopt, should_deopt);
+  bb_cursor->InsertInstructionAfter(compare, deopt_flag);
+  bb_cursor->InsertInstructionAfter(deopt, compare);
+
+  // Add receiver as input to aid CHA guard optimization later.
+  deopt_flag->AddInput(invoke_instruction->InputAt(0));
+  DCHECK_EQ(deopt_flag->InputCount(), 1u);
   deopt->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+  outermost_graph_->IncrementNumberOfCHAGuards();
 }
 
 HInstruction* HInliner::AddTypeGuard(HInstruction* receiver,
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 1e946d6..b9e284f 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1108,13 +1108,23 @@
   return HasEnvironment() ? environment_->Size() : 0;
 }
 
-void HPhi::AddInput(HInstruction* input) {
+void HVariableInputSizeInstruction::AddInput(HInstruction* input) {
   DCHECK(input->GetBlock() != nullptr);
   inputs_.push_back(HUserRecord<HInstruction*>(input));
   input->AddUseAt(this, inputs_.size() - 1);
 }
 
-void HPhi::RemoveInputAt(size_t index) {
+void HVariableInputSizeInstruction::InsertInputAt(size_t index, HInstruction* input) {
+  inputs_.insert(inputs_.begin() + index, HUserRecord<HInstruction*>(input));
+  input->AddUseAt(this, index);
+  // Update indexes in use nodes of inputs that have been pushed further back by the insert().
+  for (size_t i = index + 1u, e = inputs_.size(); i < e; ++i) {
+    DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i - 1u);
+    inputs_[i].GetUseNode()->SetIndex(i);
+  }
+}
+
+void HVariableInputSizeInstruction::RemoveInputAt(size_t index) {
   RemoveAsUserOfInput(index);
   inputs_.erase(inputs_.begin() + index);
   // Update indexes in use nodes of inputs that have been pulled forward by the erase().
@@ -1347,7 +1357,9 @@
 void HInstruction::MoveBefore(HInstruction* cursor) {
   DCHECK(!IsPhi());
   DCHECK(!IsControlFlow());
-  DCHECK(CanBeMoved());
+  DCHECK(CanBeMoved() ||
+         // HShouldDeoptimizeFlag can only be moved by CHAGuardOptimization.
+         IsShouldDeoptimizeFlag());
   DCHECK(!cursor->IsPhi());
 
   next_->previous_ = previous_;
@@ -2386,26 +2398,6 @@
   return !opt.GetDoesNotNeedDexCache();
 }
 
-void HInvokeStaticOrDirect::InsertInputAt(size_t index, HInstruction* input) {
-  inputs_.insert(inputs_.begin() + index, HUserRecord<HInstruction*>(input));
-  input->AddUseAt(this, index);
-  // Update indexes in use nodes of inputs that have been pushed further back by the insert().
-  for (size_t i = index + 1u, e = inputs_.size(); i < e; ++i) {
-    DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i - 1u);
-    inputs_[i].GetUseNode()->SetIndex(i);
-  }
-}
-
-void HInvokeStaticOrDirect::RemoveInputAt(size_t index) {
-  RemoveAsUserOfInput(index);
-  inputs_.erase(inputs_.begin() + index);
-  // Update indexes in use nodes of inputs that have been pulled forward by the erase().
-  for (size_t i = index, e = inputs_.size(); i < e; ++i) {
-    DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i + 1u);
-    inputs_[i].GetUseNode()->SetIndex(i);
-  }
-}
-
 std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs) {
   switch (rhs) {
     case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 1f886b8..1f0c8e8 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -330,6 +330,7 @@
         invoke_type_(invoke_type),
         in_ssa_form_(false),
         should_generate_constructor_barrier_(should_generate_constructor_barrier),
+        number_of_cha_guards_(0),
         instruction_set_(instruction_set),
         cached_null_constant_(nullptr),
         cached_int_constants_(std::less<int32_t>(), arena->Adapter(kArenaAllocConstantsMap)),
@@ -551,9 +552,7 @@
   }
 
   bool HasShouldDeoptimizeFlag() const {
-    // TODO: if all CHA guards can be eliminated, there is no need for the flag
-    // even if cha_single_implementation_list_ is not empty.
-    return !cha_single_implementation_list_.empty();
+    return number_of_cha_guards_ != 0;
   }
 
   bool HasTryCatch() const { return has_try_catch_; }
@@ -572,6 +571,10 @@
 
   ReferenceTypeInfo GetInexactObjectRti() const { return inexact_object_rti_; }
 
+  uint32_t GetNumberOfCHAGuards() { return number_of_cha_guards_; }
+  void SetNumberOfCHAGuards(uint32_t num) { number_of_cha_guards_ = num; }
+  void IncrementNumberOfCHAGuards() { number_of_cha_guards_++; }
+
  private:
   void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
   void RemoveDeadBlocks(const ArenaBitVector& visited);
@@ -667,6 +670,10 @@
 
   const bool should_generate_constructor_barrier_;
 
+  // Number of CHA guards in the graph. Used to short-circuit the
+  // CHA guard optimization pass when there is no CHA guard left.
+  uint32_t number_of_cha_guards_;
+
   const InstructionSet instruction_set_;
 
   // Cached constants.
@@ -2347,6 +2354,32 @@
   DISALLOW_COPY_AND_ASSIGN(HBackwardInstructionIterator);
 };
 
+class HVariableInputSizeInstruction : public HInstruction {
+ public:
+  using HInstruction::GetInputRecords;  // Keep the const version visible.
+  ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE {
+    return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
+  }
+
+  void AddInput(HInstruction* input);
+  void InsertInputAt(size_t index, HInstruction* input);
+  void RemoveInputAt(size_t index);
+
+ protected:
+  HVariableInputSizeInstruction(SideEffects side_effects,
+                                uint32_t dex_pc,
+                                ArenaAllocator* arena,
+                                size_t number_of_inputs,
+                                ArenaAllocKind kind)
+      : HInstruction(side_effects, dex_pc),
+        inputs_(number_of_inputs, arena->Adapter(kind)) {}
+
+  ArenaVector<HUserRecord<HInstruction*>> inputs_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVariableInputSizeInstruction);
+};
+
 template<size_t N>
 class HTemplateInstruction: public HInstruction {
  public:
@@ -2438,15 +2471,19 @@
   DISALLOW_COPY_AND_ASSIGN(HReturn);
 };
 
-class HPhi FINAL : public HInstruction {
+class HPhi FINAL : public HVariableInputSizeInstruction {
  public:
   HPhi(ArenaAllocator* arena,
        uint32_t reg_number,
        size_t number_of_inputs,
        Primitive::Type type,
        uint32_t dex_pc = kNoDexPc)
-      : HInstruction(SideEffects::None(), dex_pc),
-        inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
+      : HVariableInputSizeInstruction(
+            SideEffects::None(),
+            dex_pc,
+            arena,
+            number_of_inputs,
+            kArenaAllocPhiInputs),
         reg_number_(reg_number) {
     SetPackedField<TypeField>(ToPhiType(type));
     DCHECK_NE(GetType(), Primitive::kPrimVoid);
@@ -2464,14 +2501,6 @@
 
   bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
 
-  using HInstruction::GetInputRecords;  // Keep the const version visible.
-  ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
-    return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
-  }
-
-  void AddInput(HInstruction* input);
-  void RemoveInputAt(size_t index);
-
   Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
   void SetType(Primitive::Type new_type) {
     // Make sure that only valid type changes occur. The following are allowed:
@@ -2527,7 +2556,6 @@
   static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
   using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
 
-  ArenaVector<HUserRecord<HInstruction*>> inputs_;
   const uint32_t reg_number_;
 
   DISALLOW_COPY_AND_ASSIGN(HPhi);
@@ -2904,14 +2932,20 @@
 // if it's true, starts to do deoptimization.
 // It has a 4-byte slot on stack.
 // TODO: allocate a register for this flag.
-class HShouldDeoptimizeFlag FINAL : public HExpression<0> {
+class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction {
  public:
-  // TODO: use SideEffects to aid eliminating some CHA guards.
-  explicit HShouldDeoptimizeFlag(uint32_t dex_pc)
-      : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
+  // CHA guards are only optimized in a separate pass and it has no side effects
+  // with regard to other passes.
+  HShouldDeoptimizeFlag(ArenaAllocator* arena, uint32_t dex_pc)
+      : HVariableInputSizeInstruction(SideEffects::None(), dex_pc, arena, 0, kArenaAllocCHA) {
   }
 
-  // We don't eliminate CHA guards yet.
+  Primitive::Type GetType() const OVERRIDE { return Primitive::kPrimInt; }
+
+  // We do all CHA guard elimination/motion in a single pass, after which there is no
+  // further guard elimination/motion since a guard might have been used for justification
+  // of the elimination of another guard. Therefore, we pretend this guard cannot be moved
+  // to avoid other optimizations trying to move it.
   bool CanBeMoved() const OVERRIDE { return false; }
 
   DECLARE_INSTRUCTION(ShouldDeoptimizeFlag);
@@ -3791,15 +3825,10 @@
   kCanThrow  // Intrinsic may throw exceptions.
 };
 
-class HInvoke : public HInstruction {
+class HInvoke : public HVariableInputSizeInstruction {
  public:
   bool NeedsEnvironment() const OVERRIDE;
 
-  using HInstruction::GetInputRecords;  // Keep the const version visible.
-  ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE {
-    return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
-  }
-
   void SetArgumentAt(size_t index, HInstruction* argument) {
     SetRawInputAt(index, argument);
   }
@@ -3878,12 +3907,14 @@
           uint32_t dex_method_index,
           ArtMethod* resolved_method,
           InvokeType invoke_type)
-    : HInstruction(
-          SideEffects::AllExceptGCDependency(), dex_pc),  // Assume write/read on all fields/arrays.
+    : HVariableInputSizeInstruction(
+          SideEffects::AllExceptGCDependency(),  // Assume write/read on all fields/arrays.
+          dex_pc,
+          arena,
+          number_of_arguments + number_of_other_inputs,
+          kArenaAllocInvokeInputs),
       number_of_arguments_(number_of_arguments),
       resolved_method_(resolved_method),
-      inputs_(number_of_arguments + number_of_other_inputs,
-              arena->Adapter(kArenaAllocInvokeInputs)),
       dex_method_index_(dex_method_index),
       intrinsic_(Intrinsics::kNone),
       intrinsic_optimizations_(0) {
@@ -3894,7 +3925,6 @@
 
   uint32_t number_of_arguments_;
   ArtMethod* const resolved_method_;
-  ArenaVector<HUserRecord<HInstruction*>> inputs_;
   const uint32_t dex_method_index_;
   Intrinsics intrinsic_;
 
@@ -4184,10 +4214,6 @@
 
   DECLARE_INSTRUCTION(InvokeStaticOrDirect);
 
- protected:
-  void InsertInputAt(size_t index, HInstruction* input);
-  void RemoveInputAt(size_t index);
-
  private:
   static constexpr size_t kFieldClinitCheckRequirement = kNumberOfInvokePackedBits;
   static constexpr size_t kFieldClinitCheckRequirementSize =
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 0d0f62a..4bf5b08 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -54,6 +54,7 @@
 #include "base/timing_logger.h"
 #include "bounds_check_elimination.h"
 #include "builder.h"
+#include "cha_guard_optimization.h"
 #include "code_generator.h"
 #include "compiled_method.h"
 #include "compiler.h"
@@ -517,6 +518,8 @@
     return new (arena) SideEffectsAnalysis(graph);
   } else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) {
     return new (arena) HLoopOptimization(graph, most_recent_induction);
+  } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) {
+    return new (arena) CHAGuardOptimization(graph);
 #ifdef ART_ENABLE_CODEGEN_arm
   } else if (opt_name == arm::DexCacheArrayFixups::kDexCacheArrayFixupsArmPassName) {
     return new (arena) arm::DexCacheArrayFixups(graph, codegen, stats);
@@ -779,6 +782,7 @@
   InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier(
       graph, stats, "instruction_simplifier$before_codegen");
   IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, stats);
+  CHAGuardOptimization* cha_guard = new (arena) CHAGuardOptimization(graph);
 
   HOptimization* optimizations1[] = {
     intrinsics,
@@ -807,6 +811,7 @@
     fold3,  // evaluates code generated by dynamic bce
     simplify3,
     lse,
+    cha_guard,
     dce3,
     // The codegen has a few assumptions that only the instruction simplifier
     // can satisfy. For example, the code generator does not expect to see a
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index 76a94e8..453c90a 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -479,6 +479,5 @@
   MacroAssembler::B(cond, label);
 }
 
-
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index 17cf106..5661249 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -205,6 +205,15 @@
                        int32_t value,
                        vixl32::Condition cond = vixl32::al);
 
+  template <typename T>
+  vixl::aarch32::Literal<T>* CreateLiteralDestroyedWithPool(T value) {
+    vixl::aarch32::Literal<T>* literal =
+        new vixl::aarch32::Literal<T>(value,
+                                      vixl32::RawLiteral::kPlacedWhenUsed,
+                                      vixl32::RawLiteral::kDeletedOnPoolDestruction);
+    return literal;
+  }
+
  private:
   // VIXL assembler.
   ArmVIXLMacroAssembler vixl_masm_;
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 4e64f13..d07c047 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -592,7 +592,9 @@
     ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
                              vixl32::kMaxInstructionSizeInBytes,
                              CodeBufferCheckScope::kMaximumSize);
-    ___ b(ne, Narrow, exception_blocks_.back()->Entry());
+    vixl32::Label* label = exception_blocks_.back()->Entry();
+    ___ b(ne, Narrow, label);
+    ___ AddBranchLabel(label);
   }
   // TODO: think about using CBNZ here.
 }
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 50a1d9f..4e9b619 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -1717,6 +1717,11 @@
 
   __ ExceptionPoll(scratch_register, 0);
 
+  // Push the target out of range of branch emitted by ExceptionPoll.
+  for (int i = 0; i < 64; i++) {
+    __ Store(FrameOffset(2047), scratch_register, 4);
+  }
+
   __ DecreaseFrameSize(4096);
   __ DecreaseFrameSize(32);
   __ RemoveFrame(frame_size, callee_save_regs);
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 69e1d8f..b16d99a 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -5458,94 +5458,160 @@
 };
 
 const char* const VixlJniHelpersResults[] = {
-  "   0:  e92d 4de0   stmdb sp!, {r5, r6, r7, r8, sl, fp, lr}\n",
-  "   4:  ed2d 8a10   vpush {s16-s31}\n",
-  "   8:  b089        sub sp, #36 ; 0x24\n",
-  "   a:  9000        str r0, [sp, #0]\n",
-  "   c:  9121        str r1, [sp, #132]  ; 0x84\n",
-  "   e:  ed8d 0a22   vstr  s0, [sp, #136]  ; 0x88\n",
-  "  12:  9223        str r2, [sp, #140]  ; 0x8c\n",
-  "  14:  9324        str r3, [sp, #144]  ; 0x90\n",
-  "  16:  b088        sub sp, #32\n",
-  "  18:  f5ad 5d80   sub.w sp, sp, #4096 ; 0x1000\n",
-  "  1c:  9808        ldr r0, [sp, #32]\n",
-  "  1e:  981f        ldr r0, [sp, #124]  ; 0x7c\n",
-  "  20:  9821        ldr r0, [sp, #132]  ; 0x84\n",
-  "  22:  98ff        ldr r0, [sp, #1020] ; 0x3fc\n",
-  "  24:  f8dd 0400   ldr.w r0, [sp, #1024] ; 0x400\n",
-  "  28:  f8dd cffc   ldr.w ip, [sp, #4092] ; 0xffc\n",
-  "  2c:  f50d 5c80   add.w ip, sp, #4096 ; 0x1000\n",
-  "  30:  f8dc c000   ldr.w ip, [ip]\n",
-  "  34:  f8d9 c200   ldr.w ip, [r9, #512]  ; 0x200\n",
-  "  38:  f8dc 0080   ldr.w r0, [ip, #128]  ; 0x80\n",
-  "  3c:  9008        str r0, [sp, #32]\n",
-  "  3e:  901f        str r0, [sp, #124]  ; 0x7c\n",
-  "  40:  9021        str r0, [sp, #132]  ; 0x84\n",
-  "  42:  90ff        str r0, [sp, #1020] ; 0x3fc\n",
-  "  44:  f8cd 0400   str.w r0, [sp, #1024] ; 0x400\n",
-  "  48:  f8cd cffc   str.w ip, [sp, #4092] ; 0xffc\n",
-  "  4c:  f84d 5d04   str.w r5, [sp, #-4]!\n",
-  "  50:  f50d 5580   add.w r5, sp, #4096 ; 0x1000\n",
-  "  54:  f8c5 c004   str.w ip, [r5, #4]\n",
-  "  58:  f85d 5b04   ldr.w r5, [sp], #4\n",
-  "  5c:  f04f 0cff   mov.w ip, #255  ; 0xff\n",
-  "  60:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  64:  f06f 4c7f   mvn.w ip, #4278190080 ; 0xff000000\n",
-  "  68:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  6c:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  70:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  74:  900c        str r0, [sp, #48] ; 0x30\n",
-  "  76:  f8dd c030   ldr.w ip, [sp, #48] ; 0x30\n",
-  "  7a:  f8cd c034   str.w ip, [sp, #52] ; 0x34\n",
-  "  7e:  f50d 5c80   add.w ip, sp, #4096 ; 0x1000\n",
-  "  82:  f8c9 c200   str.w ip, [r9, #512]  ; 0x200\n",
-  "  86:  f8c9 d200   str.w sp, [r9, #512]  ; 0x200\n",
-  "  8a:  f8d0 c030   ldr.w ip, [r0, #48] ; 0x30\n",
-  "  8e:  47e0        blx ip\n",
-  "  90:  f8dd c02c   ldr.w ip, [sp, #44] ; 0x2c\n",
-  "  94:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  98:  f8d9 c200   ldr.w ip, [r9, #512]  ; 0x200\n",
-  "  9c:  f8cd c02c   str.w ip, [sp, #44] ; 0x2c\n",
-  "  a0:  f8dd c02c   ldr.w ip, [sp, #44] ; 0x2c\n",
-  "  a4:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  a8:  4648        mov r0, r9\n",
-  "  aa:  f8cd 9030   str.w r9, [sp, #48] ; 0x30\n",
-  "  ae:  4684        mov ip, r0\n",
-  "  b0:  f1bc 0f00   cmp.w ip, #0\n",
-  "  b4:  bf18        it  ne\n",
-  "  b6:  f10d 0c30   addne.w ip, sp, #48 ; 0x30\n",
-  "  ba:  f10d 0c30   add.w ip, sp, #48 ; 0x30\n",
-  "  be:  f1bc 0f00   cmp.w ip, #0\n",
-  "  c2:  bf0c        ite eq\n",
-  "  c4:  2000        moveq r0, #0\n",
-  "  c6:  a80c        addne r0, sp, #48 ; 0x30\n",
-  "  c8:  f8dd c040   ldr.w ip, [sp, #64] ; 0x40\n",
-  "  cc:  f1bc 0f00   cmp.w ip, #0\n",
-  "  d0:  bf18        it  ne\n",
-  "  d2:  f10d 0c40   addne.w ip, sp, #64 ; 0x40\n",
-  "  d6:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  da:  f1bc 0f00   cmp.w ip, #0\n",
-  "  de:  bf0c        ite eq\n",
-  "  e0:  2000        moveq r0, #0\n",
-  "  e2:  4668        movne r0, sp\n",
-  "  e4:  f1bc 0f00   cmp.w ip, #0\n",
-  "  e8:  bf0c        ite eq\n",
-  "  ea:  2000        moveq r0, #0\n",
-  "  ec:  f20d 4001   addwne  r0, sp, #1025 ; 0x401\n",
-  "  f0:  f1bc 0f00   cmp.w ip, #0\n",
-  "  f4:  bf18        it  ne\n",
-  "  f6:  f20d 4c01   addwne  ip, sp, #1025 ; 0x401\n",
-  "  fa:  f8d9 c084   ldr.w ip, [r9, #132]  ; 0x84\n",
-  "  fe:  f1bc 0f00   cmp.w ip, #0\n",
-  " 102:  d107        bne.n 114 <VixlJniHelpers+0x114>\n",
-  " 104:  f50d 5d80   add.w sp, sp, #4096 ; 0x1000\n",
-  " 108:  b008        add sp, #32\n",
-  " 10a:  b009        add sp, #36 ; 0x24\n",
-  " 10c:  ecbd 8a10   vpop  {s16-s31}\n",
-  " 110:  e8bd 8de0   ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
-  " 114:  4660        mov r0, ip\n",
-  " 116:  f8d9 c2b0   ldr.w ip, [r9, #688]  ; 0x2b0\n",
-  " 11a:  47e0        blx ip\n",
+  "   0:	e92d 4de0 	stmdb	sp!, {r5, r6, r7, r8, sl, fp, lr}\n",
+  "   4:	ed2d 8a10 	vpush	{s16-s31}\n",
+  "   8:	b089      	sub	sp, #36	; 0x24\n",
+  "   a:	9000      	str	r0, [sp, #0]\n",
+  "   c:	9121      	str	r1, [sp, #132]	; 0x84\n",
+  "   e:	ed8d 0a22 	vstr	s0, [sp, #136]	; 0x88\n",
+  "  12:	9223      	str	r2, [sp, #140]	; 0x8c\n",
+  "  14:	9324      	str	r3, [sp, #144]	; 0x90\n",
+  "  16:	b088      	sub	sp, #32\n",
+  "  18:	f5ad 5d80 	sub.w	sp, sp, #4096	; 0x1000\n",
+  "  1c:	9808      	ldr	r0, [sp, #32]\n",
+  "  1e:	981f      	ldr	r0, [sp, #124]	; 0x7c\n",
+  "  20:	9821      	ldr	r0, [sp, #132]	; 0x84\n",
+  "  22:	98ff      	ldr	r0, [sp, #1020]	; 0x3fc\n",
+  "  24:	f8dd 0400 	ldr.w	r0, [sp, #1024]	; 0x400\n",
+  "  28:	f8dd cffc 	ldr.w	ip, [sp, #4092]	; 0xffc\n",
+  "  2c:	f50d 5c80 	add.w	ip, sp, #4096	; 0x1000\n",
+  "  30:	f8dc c000 	ldr.w	ip, [ip]\n",
+  "  34:	f8d9 c200 	ldr.w	ip, [r9, #512]	; 0x200\n",
+  "  38:	f8dc 0080 	ldr.w	r0, [ip, #128]	; 0x80\n",
+  "  3c:	9008      	str	r0, [sp, #32]\n",
+  "  3e:	901f      	str	r0, [sp, #124]	; 0x7c\n",
+  "  40:	9021      	str	r0, [sp, #132]	; 0x84\n",
+  "  42:	90ff      	str	r0, [sp, #1020]	; 0x3fc\n",
+  "  44:	f8cd 0400 	str.w	r0, [sp, #1024]	; 0x400\n",
+  "  48:	f8cd cffc 	str.w	ip, [sp, #4092]	; 0xffc\n",
+  "  4c:	f84d 5d04 	str.w	r5, [sp, #-4]!\n",
+  "  50:	f50d 5580 	add.w	r5, sp, #4096	; 0x1000\n",
+  "  54:	f8c5 c004 	str.w	ip, [r5, #4]\n",
+  "  58:	f85d 5b04 	ldr.w	r5, [sp], #4\n",
+  "  5c:	f04f 0cff 	mov.w	ip, #255	; 0xff\n",
+  "  60:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  64:	f06f 4c7f 	mvn.w	ip, #4278190080	; 0xff000000\n",
+  "  68:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  6c:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  70:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  74:	900c      	str	r0, [sp, #48]	; 0x30\n",
+  "  76:	f8dd c030 	ldr.w	ip, [sp, #48]	; 0x30\n",
+  "  7a:	f8cd c034 	str.w	ip, [sp, #52]	; 0x34\n",
+  "  7e:	f50d 5c80 	add.w	ip, sp, #4096	; 0x1000\n",
+  "  82:	f8c9 c200 	str.w	ip, [r9, #512]	; 0x200\n",
+  "  86:	f8c9 d200 	str.w	sp, [r9, #512]	; 0x200\n",
+  "  8a:	f8d0 c030 	ldr.w	ip, [r0, #48]	; 0x30\n",
+  "  8e:	47e0      	blx	ip\n",
+  "  90:	f8dd c02c 	ldr.w	ip, [sp, #44]	; 0x2c\n",
+  "  94:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  98:	f8d9 c200 	ldr.w	ip, [r9, #512]	; 0x200\n",
+  "  9c:	f8cd c02c 	str.w	ip, [sp, #44]	; 0x2c\n",
+  "  a0:	f8dd c02c 	ldr.w	ip, [sp, #44]	; 0x2c\n",
+  "  a4:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  a8:	4648      	mov	r0, r9\n",
+  "  aa:	f8cd 9030 	str.w	r9, [sp, #48]	; 0x30\n",
+  "  ae:	4684      	mov	ip, r0\n",
+  "  b0:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  b4:	bf18      	it	ne\n",
+  "  b6:	f10d 0c30 	addne.w	ip, sp, #48	; 0x30\n",
+  "  ba:	f10d 0c30 	add.w	ip, sp, #48	; 0x30\n",
+  "  be:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  c2:	bf0c      	ite	eq\n",
+  "  c4:	2000      	moveq	r0, #0\n",
+  "  c6:	a80c      	addne	r0, sp, #48	; 0x30\n",
+  "  c8:	f8dd c040 	ldr.w	ip, [sp, #64]	; 0x40\n",
+  "  cc:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  d0:	bf18      	it	ne\n",
+  "  d2:	f10d 0c40 	addne.w	ip, sp, #64	; 0x40\n",
+  "  d6:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  da:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  de:	bf0c      	ite	eq\n",
+  "  e0:	2000      	moveq	r0, #0\n",
+  "  e2:	4668      	movne	r0, sp\n",
+  "  e4:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  e8:	bf0c      	ite	eq\n",
+  "  ea:	2000      	moveq	r0, #0\n",
+  "  ec:	f20d 4001 	addwne	r0, sp, #1025	; 0x401\n",
+  "  f0:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  f4:	bf18      	it	ne\n",
+  "  f6:	f20d 4c01 	addwne	ip, sp, #1025	; 0x401\n",
+  "  fa:	f8d9 c084 	ldr.w	ip, [r9, #132]	; 0x84\n",
+  "  fe:	f1bc 0f00 	cmp.w	ip, #0\n",
+  " 102:	d16f      	bne.n	1e4 <VixlJniHelpers+0x1e4>\n",
+  " 104:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 108:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 10c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 110:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 114:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 118:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 11c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 120:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 124:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 128:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 12c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 130:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 134:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 138:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 13c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 140:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 144:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 148:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 14c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 150:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 154:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 158:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 15c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 160:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 164:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 168:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 16c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 170:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 174:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 178:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 17c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 180:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 184:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 188:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 18c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 190:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 194:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 198:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 19c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1a0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1a4:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1a8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1ac:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1b0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1b4:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1b8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1bc:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1c0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1c4:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1c8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1cc:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1d0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1d4:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1d8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1dc:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1e0:	f000 b802 	b.w	1e8 <VixlJniHelpers+0x1e8>\n",
+  " 1e4:	f000 b81a 	b.w	21c <VixlJniHelpers+0x21c>\n",
+  " 1e8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1ec:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1f0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1f4:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1f8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1fc:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 200:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 204:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 208:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 20c:	f50d 5d80 	add.w	sp, sp, #4096	; 0x1000\n",
+  " 210:	b008      	add	sp, #32\n",
+  " 212:	b009      	add	sp, #36	; 0x24\n",
+  " 214:	ecbd 8a10 	vpop	{s16-s31}\n",
+  " 218:	e8bd 8de0 	ldmia.w	sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
+  " 21c:	4660      	mov	r0, ip\n",
+  " 21e:	f8d9 c2b0 	ldr.w	ip, [r9, #688]	; 0x2b0\n",
+  " 222:	47e0      	blx	ip\n",
   nullptr
 };
 
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 5cdf671..61e0aab 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -83,18 +83,19 @@
   "GraphChecker ",
   "Verifier     ",
   "CallingConv  ",
+  "CHA          ",
 };
 
 template <bool kCount>
 ArenaAllocatorStatsImpl<kCount>::ArenaAllocatorStatsImpl()
-    : num_allocations_(0u) {
-  std::fill_n(alloc_stats_, arraysize(alloc_stats_), 0u);
+    : num_allocations_(0u),
+      alloc_stats_(kNumArenaAllocKinds, 0u) {
 }
 
 template <bool kCount>
 void ArenaAllocatorStatsImpl<kCount>::Copy(const ArenaAllocatorStatsImpl& other) {
   num_allocations_ = other.num_allocations_;
-  std::copy(other.alloc_stats_, other.alloc_stats_ + arraysize(alloc_stats_), alloc_stats_);
+  std::copy_n(other.alloc_stats_.begin(), kNumArenaAllocKinds, alloc_stats_.begin());
 }
 
 template <bool kCount>
@@ -111,7 +112,7 @@
 template <bool kCount>
 size_t ArenaAllocatorStatsImpl<kCount>::BytesAllocated() const {
   const size_t init = 0u;  // Initial value of the correct type.
-  return std::accumulate(alloc_stats_, alloc_stats_ + arraysize(alloc_stats_), init);
+  return std::accumulate(alloc_stats_.begin(), alloc_stats_.end(), init);
 }
 
 template <bool kCount>
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 2feb28a..6c764cb 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -21,6 +21,7 @@
 #include <stddef.h>
 
 #include "base/bit_utils.h"
+#include "base/dchecked_vector.h"
 #include "base/memory_tool.h"
 #include "debug_stack.h"
 #include "macros.h"
@@ -132,8 +133,7 @@
 
  private:
   size_t num_allocations_;
-  // TODO: Use std::array<size_t, kNumArenaAllocKinds> from C++11 when we upgrade the STL.
-  size_t alloc_stats_[kNumArenaAllocKinds];  // Bytes used by various allocation kinds.
+  dchecked_vector<size_t> alloc_stats_;  // Bytes used by various allocation kinds.
 
   static const char* const kAllocNames[];
 };
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 20aaa42..5b8d4e4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2503,8 +2503,9 @@
       // the Java-side could still succeed for racy programs if another thread is actively
       // modifying the class loader's path list.
 
-      if (Runtime::Current()->IsAotCompiler()) {
-        // Oops, compile-time, can't run actual class-loader code.
+      if (!self->CanCallIntoJava()) {
+        // Oops, we can't call into java so we can't run actual class-loader code.
+        // This is true for e.g. for the compiler (jit or aot).
         ObjPtr<mirror::Throwable> pre_allocated =
             Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
         self->SetException(pre_allocated);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index dac2e60..b7125a8 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -291,7 +291,7 @@
   // is not null when we instrument.
   thread_pool_.reset(new ThreadPool("Jit thread pool", 1));
   thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority);
-  thread_pool_->StartWorkers(Thread::Current());
+  Start();
 }
 
 void Jit::DeleteThreadPool() {
@@ -710,14 +710,23 @@
   }
 }
 
+void Jit::Stop() {
+  Thread* self = Thread::Current();
+  // TODO(ngeoffray): change API to not require calling WaitForCompilationToFinish twice.
+  WaitForCompilationToFinish(self);
+  GetThreadPool()->StopWorkers(self);
+  WaitForCompilationToFinish(self);
+}
+
+void Jit::Start() {
+  GetThreadPool()->StartWorkers(Thread::Current());
+}
+
 ScopedJitSuspend::ScopedJitSuspend() {
   jit::Jit* jit = Runtime::Current()->GetJit();
   was_on_ = (jit != nullptr) && (jit->GetThreadPool() != nullptr);
   if (was_on_) {
-    Thread* self = Thread::Current();
-    jit->WaitForCompilationToFinish(self);
-    jit->GetThreadPool()->StopWorkers(self);
-    jit->WaitForCompilationToFinish(self);
+    jit->Stop();
   }
 }
 
@@ -725,7 +734,7 @@
   if (was_on_) {
     DCHECK(Runtime::Current()->GetJit() != nullptr);
     DCHECK(Runtime::Current()->GetJit()->GetThreadPool() != nullptr);
-    Runtime::Current()->GetJit()->GetThreadPool()->StartWorkers(Thread::Current());
+    Runtime::Current()->GetJit()->Start();
   }
 }
 
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index a230c78..05c3905 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -179,6 +179,12 @@
     return thread_pool_.get();
   }
 
+  // Stop the JIT by waiting for all current compilations and enqueued compilations to finish.
+  void Stop();
+
+  // Start JIT threads.
+  void Start();
+
  private:
   Jit();
 
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index e70547d..68815e7 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -396,19 +396,14 @@
   }
 }
 
-// Performs updates to class that will allow us to verify it.
-bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
-                            art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
+bool Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+                              art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+                              const art::DexFile::ClassDef& class_def) {
   art::ClassLinker* linker = runtime_->GetClassLinker();
   art::PointerSize image_pointer_size = linker->GetImagePointerSize();
-  const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
-      *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_));
-  if (class_def == nullptr) {
-    RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!");
-    return false;
-  }
-  const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def->class_idx_);
+  const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def.class_idx_);
   const art::DexFile& old_dex_file = mclass->GetDexFile();
+  // Update methods.
   for (art::ArtMethod& method : mclass->GetMethods(image_pointer_size)) {
     const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(method.GetName());
     art::dex::TypeIndex method_return_idx =
@@ -435,10 +430,54 @@
     uint32_t dex_method_idx = dex_file_->GetIndexForMethodId(*method_id);
     method.SetDexMethodIndex(dex_method_idx);
     linker->SetEntryPointsToInterpreter(&method);
-    method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(*class_def, dex_method_idx));
+    method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx));
     method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size);
     method.SetDexCacheResolvedTypes(new_dex_cache->GetResolvedTypes(), image_pointer_size);
   }
+  return true;
+}
+
+bool Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) {
+  // TODO The IFields & SFields pointers should be combined like the methods_ arrays were.
+  for (auto fields_iter : {mclass->GetIFields(), mclass->GetSFields()}) {
+    for (art::ArtField& field : fields_iter) {
+      std::string declaring_class_name;
+      const art::DexFile::TypeId* new_declaring_id =
+          dex_file_->FindTypeId(field.GetDeclaringClass()->GetDescriptor(&declaring_class_name));
+      const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(field.GetName());
+      const art::DexFile::TypeId* new_type_id = dex_file_->FindTypeId(field.GetTypeDescriptor());
+      // TODO Handle error, cleanup.
+      CHECK(new_name_id != nullptr && new_type_id != nullptr && new_declaring_id != nullptr);
+      const art::DexFile::FieldId* new_field_id =
+          dex_file_->FindFieldId(*new_declaring_id, *new_name_id, *new_type_id);
+      CHECK(new_field_id != nullptr);
+      // We only need to update the index since the other data in the ArtField cannot be updated.
+      field.SetDexFieldIndex(dex_file_->GetIndexForFieldId(*new_field_id));
+    }
+  }
+  return true;
+}
+
+// Performs updates to class that will allow us to verify it.
+bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
+                            art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
+  const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
+      *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_));
+  if (class_def == nullptr) {
+    RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!");
+    return false;
+  }
+  if (!UpdateMethods(mclass, new_dex_cache, *class_def)) {
+    // TODO Investigate appropriate error types.
+    RecordFailure(ERR(INTERNAL), "Unable to update class methods.");
+    return false;
+  }
+  if (!UpdateFields(mclass)) {
+    // TODO Investigate appropriate error types.
+    RecordFailure(ERR(INTERNAL), "Unable to update class fields.");
+    return false;
+  }
+
   // Update the class fields.
   // Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
   // to call GetReturnTypeDescriptor and GetParameterTypeList above).
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index c819acd..73cfc2b 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -159,6 +159,14 @@
                           art::ObjPtr<art::mirror::LongArray> original_cookie)
       REQUIRES(art::Locks::mutator_lock_);
 
+  bool UpdateFields(art::ObjPtr<art::mirror::Class> mclass)
+      REQUIRES(art::Locks::mutator_lock_);
+
+  bool UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+                     art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+                     const art::DexFile::ClassDef& class_def)
+      REQUIRES(art::Locks::mutator_lock_);
+
   bool UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
                    art::ObjPtr<art::mirror::DexCache> new_dex_cache)
       REQUIRES(art::Locks::mutator_lock_);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 0977093..2086d70 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1199,6 +1199,8 @@
   CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId);
   CHECK(self != nullptr);
 
+  self->SetCanCallIntoJava(!IsAotCompiler());
+
   // Set us to runnable so tools using a runtime can allocate and GC by default
   self->TransitionFromSuspendedToRunnable();
 
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 9c93a5f..aff12ff 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1807,7 +1807,11 @@
   }
 }
 
-Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), interrupted_(false) {
+Thread::Thread(bool daemon)
+    : tls32_(daemon),
+      wait_monitor_(nullptr),
+      interrupted_(false),
+      can_call_into_java_(true) {
   wait_mutex_ = new Mutex("a thread wait mutex");
   wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
   tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
diff --git a/runtime/thread.h b/runtime/thread.h
index 31cd0eb..411d85f 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -881,6 +881,15 @@
     --tls32_.disable_thread_flip_count;
   }
 
+  // Returns true if the thread is allowed to call into java.
+  bool CanCallIntoJava() const {
+    return can_call_into_java_;
+  }
+
+  void SetCanCallIntoJava(bool can_call_into_java) {
+    can_call_into_java_ = can_call_into_java;
+  }
+
   // Activates single step control for debugging. The thread takes the
   // ownership of the given SingleStepControl*. It is deleted by a call
   // to DeactivateSingleStepControl or upon thread destruction.
@@ -1588,6 +1597,10 @@
   // Pending extra checkpoints if checkpoint_function_ is already used.
   std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_);
 
+  // True if the thread is allowed to call back into java (for e.g. during class resolution).
+  // By default this is true.
+  bool can_call_into_java_;
+
   friend class Dbg;  // For SetStateUnsafe.
   friend class gc::collector::SemiSpace;  // For getting stack traces.
   friend class Runtime;  // For CreatePeer.
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 6abdca1..d9179c3 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -90,6 +90,8 @@
   Runtime* runtime = Runtime::Current();
   CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, nullptr, false));
   worker->thread_ = Thread::Current();
+  // Thread pool workers cannot call into java.
+  worker->thread_->SetCanCallIntoJava(false);
   // Do work until its time to shut down.
   worker->Run();
   runtime->DetachCurrentThread();
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index eaadfe0..7ecfcd1 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -80,6 +80,7 @@
   DISALLOW_COPY_AND_ASSIGN(ThreadPoolWorker);
 };
 
+// Note that thread pool workers will set Thread#setCanCallIntoJava to false.
 class ThreadPool {
  public:
   // Returns the number of threads in the thread pool.
diff --git a/test/141-class-unload/expected.txt b/test/141-class-unload/expected.txt
index 0a03ecb..a1c5fa8 100644
--- a/test/141-class-unload/expected.txt
+++ b/test/141-class-unload/expected.txt
@@ -20,5 +20,6 @@
 JNI_OnLoad called
 class null false test
 JNI_OnUnload called
+JNI_OnLoad called
 Number of loaded unload-ex maps 0
 Too small false
diff --git a/test/141-class-unload/jni_unload.cc b/test/141-class-unload/jni_unload.cc
index bbbb0a6..9b7e171 100644
--- a/test/141-class-unload/jni_unload.cc
+++ b/test/141-class-unload/jni_unload.cc
@@ -32,5 +32,19 @@
   }
 }
 
+extern "C" JNIEXPORT void JNICALL Java_Main_stopJit(JNIEnv*, jclass) {
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit != nullptr) {
+    jit->Stop();
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_startJit(JNIEnv*, jclass) {
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit != nullptr) {
+    jit->Start();
+  }
+}
+
 }  // namespace
 }  // namespace art
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 2a6e944..595c70d 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -55,11 +55,15 @@
     }
 
     private static void testOatFilesUnloaded(int pid) throws Exception {
+        System.loadLibrary(nativeLibraryName);
+        // Stop the JIT to ensure its threads and work queue are not keeping classes
+        // artifically alive.
+        stopJit();
+        Runtime.getRuntime().gc();
+        System.runFinalization();
         BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps"));
         String line;
         int count = 0;
-        Runtime.getRuntime().gc();
-        System.runFinalization();
         while ((line = reader.readLine()) != null) {
             if (line.contains("@141-class-unload-ex.jar")) {
                 System.out.println(line);
@@ -67,6 +71,7 @@
             }
         }
         System.out.println("Number of loaded unload-ex maps " + count);
+        startJit();
     }
 
     private static void stressTest(Constructor<?> constructor) throws Exception {
@@ -229,4 +234,7 @@
     private static int getPid() throws Exception {
       return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
     }
+
+    public static native void stopJit();
+    public static native void startJit();
 }
diff --git a/test/530-checker-loops3/src/Main.java b/test/530-checker-loops3/src/Main.java
index 209786a..dfc4a5f 100644
--- a/test/530-checker-loops3/src/Main.java
+++ b/test/530-checker-loops3/src/Main.java
@@ -235,6 +235,59 @@
     }
   }
 
+  /// CHECK-START: void Main.shortBound1(int[], short) BCE (before)
+  /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+  //
+  /// CHECK-START: void Main.shortBound1(int[], short) BCE (after)
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-NOT: Deoptimize
+  //
+  /// CHECK-START: void Main.shortBound1(int[], short) BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  public static void shortBound1(int[] array, short s) {
+    // Lower precision bound will appear in deopt arithmetic
+    // and follows normal implicit widening conversion.
+    for (int i = 0; i < s; i++) {
+      array[i] = 222;
+    }
+  }
+
+  /// CHECK-START: void Main.shortBound2(int[], short) BCE (before)
+  /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+  //
+  /// CHECK-START: void Main.shortBound2(int[], short) BCE (after)
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-NOT: Deoptimize
+  //
+  /// CHECK-START: void Main.shortBound2(int[], short) BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  public static void shortBound2(int[] array, short s) {
+    // Lower precision bound will appear in deopt arithmetic
+    // and follows normal implicit widening conversion.
+    for (int i = 0; s > i; i++) {
+      array[i] = 444;
+    }
+  }
+
+  /// CHECK-START: void Main.narrowingFromLong(int[], int) BCE (before)
+  /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+  //
+  /// CHECK-START: void Main.narrowingFromLong(int[], int) BCE (after)
+  /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+  public static void narrowingFromLong(int[] array, int n) {
+    // Parallel induction in long precision that is narrowed provides type
+    // conversion challenges for BCE in deopt arithmetic when combined
+    // with the int loop induction. Therefore, currently skipped.
+    long l = 0;
+    for (int i = 0; i < n; i++, l++) {
+      array[(int)l] = 888;
+    }
+  }
+
   //
   // Verifier.
   //
@@ -316,6 +369,38 @@
     } catch (ArrayIndexOutOfBoundsException e) {
     }
 
+    shortBound1(a, (short)a.length);
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(222, a[i]);
+    }
+    shortBound2(a, (short)a.length);
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(444, a[i]);
+    }
+
+    try {
+      shortBound1(a, (short)(a.length + 1));
+      throw new Error("Should throw AIOOBE");
+    } catch (ArrayIndexOutOfBoundsException e) {
+    }
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(222, a[i]);
+    }
+
+    try {
+      shortBound2(a, (short)(a.length + 1));
+      throw new Error("Should throw AIOOBE");
+    } catch (ArrayIndexOutOfBoundsException e) {
+    }
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(444, a[i]);
+    }
+
+    narrowingFromLong(a, a.length);
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(888, a[i]);
+    }
+
     System.out.println("passed");
   }
 
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index ecc129a..ad3ff44 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -465,6 +465,20 @@
     return i;
   }
 
+  // TODO: handle as closed/empty eventually?
+  static int mainIndexShort1(short s) {
+    int i = 0;
+    for (i = 0; i < s; i++) { }
+    return i;
+  }
+
+  // TODO: handle as closed/empty eventually?
+  static int mainIndexShort2(short s) {
+    int i = 0;
+    for (i = 0; s > i; i++) { }
+    return i;
+  }
+
   /// 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
@@ -693,6 +707,75 @@
     return x;
   }
 
+  /// CHECK-START: float Main.periodicFloat10() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi4:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  //
+  /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  //
+  /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+  /// CHECK-DAG: <<Float:f\d+>>  FloatConstant 2    loop:none
+  /// CHECK-DAG:                 Return [<<Float>>] loop:none
+  private static float periodicFloat10() {
+    float r = 4.5f;
+    float s = 2.0f;
+    float t = -1.0f;
+    for (int i = 0; i < 10; i++) {
+      float tmp = t; t = r; r = s; s = tmp;
+    }
+    return r;
+  }
+
+  /// CHECK-START: float Main.periodicFloat11() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi4:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  //
+  /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  //
+  /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+  /// CHECK-DAG: <<Float:f\d+>>  FloatConstant -1   loop:none
+  /// CHECK-DAG:                 Return [<<Float>>] loop:none
+  private static float periodicFloat11() {
+    float r = 4.5f;
+    float s = 2.0f;
+    float t = -1.0f;
+    for (int i = 0; i < 11; i++) {
+      float tmp = t; t = r; r = s; s = tmp;
+    }
+    return r;
+  }
+
+  /// CHECK-START: float Main.periodicFloat12() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi4:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  //
+  /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  //
+  /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+  /// CHECK-DAG: <<Float:f\d+>>  FloatConstant 4.5  loop:none
+  /// CHECK-DAG:                 Return [<<Float>>] loop:none
+  private static float periodicFloat12() {
+    float r = 4.5f;
+    float s = 2.0f;
+    float t = -1.0f;
+    for (int i = 0; i < 12; i++) {
+      float tmp = t; t = r; r = s; s = tmp;
+    }
+    return r;
+  }
+
   private static int exceptionExitBeforeAdd() {
     int k = 0;
     try {
@@ -779,6 +862,8 @@
     for (int n = -4; n < 4; n++) {
       int tc = (n <= 0) ? 0 : n;
       expectEquals(tc, mainIndexReturnedN(n));
+      expectEquals(tc, mainIndexShort1((short) n));
+      expectEquals(tc, mainIndexShort2((short) n));
       expectEquals(tc & 1, periodicReturnedN(n));
       expectEquals((tc * (tc + 1)) / 2, getSumN(n));
     }
@@ -803,6 +888,10 @@
       expectEquals(!even, periodicBoolIdiom3N(false, n));
     }
 
+    expectEquals( 2.0f, periodicFloat10());
+    expectEquals(-1.0f, periodicFloat11());
+    expectEquals( 4.5f, periodicFloat12());
+
     expectEquals(100, exceptionExitBeforeAdd());
     expectEquals(100, exceptionExitAfterAdd());
     a = null;
@@ -815,6 +904,12 @@
     System.out.println("passed");
   }
 
+  private static void expectEquals(float expected, float result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
   private static void expectEquals(int expected, int result) {
     if (expected != result) {
       throw new Error("Expected: " + expected + ", found: " + result);
diff --git a/test/Android.arm_vixl.mk b/test/917-fields-transformation/build
old mode 100644
new mode 100755
similarity index 71%
rename from test/Android.arm_vixl.mk
rename to test/917-fields-transformation/build
index c89eb4a..898e2e5
--- a/test/Android.arm_vixl.mk
+++ b/test/917-fields-transformation/build
@@ -1,5 +1,6 @@
+#!/bin/bash
 #
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright 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.
@@ -12,9 +13,5 @@
 # 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.
-#
 
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
-  562-checker-no-intermediate \
-  624-checker-stringops \
+./default-build "$@" --experimental agents
diff --git a/test/917-fields-transformation/expected.txt b/test/917-fields-transformation/expected.txt
new file mode 100644
index 0000000..bcdd201
--- /dev/null
+++ b/test/917-fields-transformation/expected.txt
@@ -0,0 +1,12 @@
+Result is Hello
+take1 is Hello
+take2 is Goodbye
+Result is start
+take1 is start
+take2 is end
+Result is Goodbye
+take1 is Hello
+take2 is Goodbye
+Result is end
+take1 is start
+take2 is end
diff --git a/test/917-fields-transformation/info.txt b/test/917-fields-transformation/info.txt
new file mode 100644
index 0000000..4cd1bd9
--- /dev/null
+++ b/test/917-fields-transformation/info.txt
@@ -0,0 +1 @@
+Tests field access after class redefinition support in the jvmti plugin.
diff --git a/test/917-fields-transformation/run b/test/917-fields-transformation/run
new file mode 100755
index 0000000..a434b63
--- /dev/null
+++ b/test/917-fields-transformation/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if  [[ "$@" == *"-O"* ]]; then
+  agent=libtiagent.so
+  plugin=libopenjdkjvmti.so
+  lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+  arg="jvm"
+else
+  arg="art"
+  if [[ "$@" != *"--debuggable"* ]]; then
+    other_args=" -Xcompiler-option --debuggable "
+  else
+    other_args=""
+  fi
+fi
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:${agent}=917-fields-transformation,${arg} \
+                   --android-runtime-option -Xplugin:${plugin} \
+                   --android-runtime-option -Xfully-deoptable \
+                   ${other_args} \
+                   --args ${lib}
diff --git a/test/917-fields-transformation/src/Main.java b/test/917-fields-transformation/src/Main.java
new file mode 100644
index 0000000..5378bb7
--- /dev/null
+++ b/test/917-fields-transformation/src/Main.java
@@ -0,0 +1,80 @@
+/*
+ * 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.util.Base64;
+public class Main {
+
+  // base64 encoded class/dex file for
+  // class Transform {
+  //   public String take1;
+  //   public String take2;
+  //
+  //   public Transform(String a, String b) {
+  //     take1 = a;
+  //     take2 = b;
+  //   }
+  //
+  //   public String getResult() {
+  //     return take2;
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAFwoABQARCQAEABIJAAQAEwcAFAcAFQEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" +
+    "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" +
+    "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" +
+    "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkAFgwABgAHDAAIAAcBAAlU" +
+    "cmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQADKClWACAABAAFAAAAAgABAAYABwAAAAEACAAH" +
+    "AAAAAgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAU" +
+    "AAQAFQAJABYADgAXAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAAGgAB" +
+    "AA8AAAACABA=");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAGUTBb4jIABRlaI9rejdk7RCfyqR2kmNSkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAM" +
+    "AAAAcAAAAAQAAACgAAAAAwAAALAAAAACAAAA1AAAAAMAAADkAAAAAQAAAPwAAACIAQAAHAEAAFwB" +
+    "AABkAQAAZwEAAHQBAACIAQAAnAEAAKwBAACvAQAAtAEAAMgBAADTAQAA2gEAAAIAAAADAAAABAAA" +
+    "AAYAAAABAAAAAgAAAAAAAAAGAAAAAwAAAAAAAAAHAAAAAwAAAFQBAAAAAAIACgAAAAAAAgALAAAA" +
+    "AAACAAAAAAAAAAAACQAAAAEAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAUAAAAAAAAA8AEAAAAAAAAD" +
+    "AAMAAQAAAOEBAAAIAAAAcBACAAAAWwEAAFsCAQAOAAIAAQAAAAAA6wEAAAMAAABUEAEAEQAAAAIA" +
+    "AAACAAIABjxpbml0PgABTAALTFRyYW5zZm9ybTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEv" +
+    "bGFuZy9TdHJpbmc7AA5UcmFuc2Zvcm0uamF2YQABVgADVkxMABJlbWl0dGVyOiBqYWNrLTQuMTkA" +
+    "CWdldFJlc3VsdAAFdGFrZTEABXRha2UyABQCAAAHDjwtLQAaAAcOAAACAQEAAQEBAIGABJwCAQG8" +
+    "AgAADQAAAAAAAAABAAAAAAAAAAEAAAAMAAAAcAAAAAIAAAAEAAAAoAAAAAMAAAADAAAAsAAAAAQA" +
+    "AAACAAAA1AAAAAUAAAADAAAA5AAAAAYAAAABAAAA/AAAAAEgAAACAAAAHAEAAAEQAAABAAAAVAEA" +
+    "AAIgAAAMAAAAXAEAAAMgAAACAAAA4QEAAAAgAAABAAAA8AEAAAAQAAABAAAABAIAAA==");
+
+  public static void main(String[] args) {
+    System.loadLibrary(args[1]);
+    doTest(new Transform("Hello", "Goodbye"),
+           new Transform("start", "end"));
+  }
+
+  private static void printTransform(Transform t) {
+    System.out.println("Result is " + t.getResult());
+    System.out.println("take1 is " + t.take1);
+    System.out.println("take2 is " + t.take2);
+  }
+  public static void doTest(Transform t1, Transform t2) {
+    printTransform(t1);
+    printTransform(t2);
+    doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    printTransform(t1);
+    printTransform(t2);
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] class_file,
+                                                       byte[] dex_file);
+}
diff --git a/test/917-fields-transformation/src/Transform.java b/test/917-fields-transformation/src/Transform.java
new file mode 100644
index 0000000..6fe6223
--- /dev/null
+++ b/test/917-fields-transformation/src/Transform.java
@@ -0,0 +1,29 @@
+/*
+ * 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 Transform {
+  public String take1;
+  public String take2;
+
+  public Transform(String take1, String take2) {
+    this.take1 = take1;
+    this.take2 = take2;
+  }
+
+  public String getResult() {
+    return take1;
+  }
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index c02999b..543ac04 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -226,6 +226,10 @@
 $(shell echo $(1) | tr '[:lower:]' '[:upper:]' | tr '-' '_')
 endef  # name-to-var
 
+# Disable 153-reference-stress temporarily until a fix arrives. b/33389022.
+ART_TEST_RUN_TEST_SKIP += \
+  153-reference-stress
+
 ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
         $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
         $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES))
@@ -280,6 +284,7 @@
   911-get-stack-trace \
   912-classes \
   913-heaps \
+  917-fields-transformation \
 
 ifneq (,$(filter target,$(TARGET_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -564,26 +569,6 @@
       $(TEST_ART_BROKEN_OPTIMIZING_GRAPH_COLOR),$(ALL_ADDRESS_SIZES))
 endif
 
-# Known broken tests for the ARM VIXL backend.
-# Android.arm_vixl.mk defines TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS.
-include $(LOCAL_PATH)/Android.arm_vixl.mk
-
-ifdef ART_USE_VIXL_ARM_BACKEND
-  ifeq (arm,$(filter arm,$(TARGET_ARCH) $(TARGET_2ND_ARCH)))
-    ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES)))
-      ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
-          $(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
-          $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \
-          $(TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS),32)
-    endif
-  endif
-  # TODO(VIXL): These two tests currently fail, but adding them to `ART_TEST_KNOWN_BROKEN` breaks
-  # `export ART_USE_VIXL_ARM_BACKEND=true && mma -j6 test-art-target-gtest dist`
-  #ART_TEST_KNOWN_BROKEN += test-art-target-gtest-dex2oat_test32
-  #ART_TEST_KNOWN_BROKEN += test-art-target-gtest-image_test32
-endif
-
-
 # Known broken tests for the mips32 optimizing compiler backend.
 TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS := \
 
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 2795cbc..3886148 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -66,6 +66,7 @@
   { "911-get-stack-trace", Test911GetStackTrace::OnLoad, nullptr },
   { "912-classes", Test912Classes::OnLoad, nullptr },
   { "913-heaps", Test913Heaps::OnLoad, nullptr },
+  { "917-fields-transformation", common_redefine::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {