Merge "Fixed a few bugs in dexfuzz:"
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 669d8cd..9d39bf2 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -103,6 +103,17 @@
   return (it != verified_methods_.end()) ? it->second : nullptr;
 }
 
+void VerificationResults::CreateVerifiedMethodFor(MethodReference ref) {
+  // This method should only be called for classes verified at compile time,
+  // which have no verifier error, nor has methods that we know will throw
+  // at runtime.
+  AtomicMap::InsertResult result = atomic_verified_methods_.Insert(
+      ref,
+      /*expected*/ nullptr,
+      new VerifiedMethod(/* encountered_error_types */ 0, /* has_runtime_throw */ false));
+  DCHECK_EQ(result, AtomicMap::kInsertResultSuccess);
+}
+
 void VerificationResults::AddRejectedClass(ClassReference ref) {
   {
     WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index ea38f4d..22749fa 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -32,6 +32,7 @@
 
 namespace verifier {
 class MethodVerifier;
+class VerifierDepsTest;
 }  // namespace verifier
 
 class CompilerOptions;
@@ -47,6 +48,9 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!verified_methods_lock_);
 
+  void CreateVerifiedMethodFor(MethodReference ref)
+      REQUIRES(!verified_methods_lock_);
+
   const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
       REQUIRES(!verified_methods_lock_);
 
@@ -77,6 +81,8 @@
   // Rejected classes.
   ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
+
+  friend class verifier::VerifierDepsTest;
 };
 
 }  // namespace art
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 04331e5..ce53417 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -32,6 +32,8 @@
 
 class VerifiedMethod {
  public:
+  VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw);
+
   // Cast elision set type.
   // Since we're adding the dex PCs to the set in increasing order, a sorted vector
   // is better for performance (not just memory usage), especially for large sets.
@@ -80,8 +82,6 @@
   }
 
  private:
-  VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw);
-
   /*
    * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
    * verification). For type-precise determination we have all the data we need, so we just need to
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 6b62110..a2bab80 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2005,6 +2005,35 @@
   }
 }
 
+static void PopulateVerifiedMethods(const DexFile& dex_file,
+                                    uint32_t class_def_index,
+                                    VerificationResults* verification_results) {
+  const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+  const uint8_t* class_data = dex_file.GetClassData(class_def);
+  if (class_data == nullptr) {
+    return;
+  }
+  ClassDataItemIterator it(dex_file, class_data);
+  // Skip fields
+  while (it.HasNextStaticField()) {
+    it.Next();
+  }
+  while (it.HasNextInstanceField()) {
+    it.Next();
+  }
+
+  while (it.HasNextDirectMethod()) {
+    verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex()));
+    it.Next();
+  }
+
+  while (it.HasNextVirtualMethod()) {
+    verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex()));
+    it.Next();
+  }
+  DCHECK(!it.HasNext());
+}
+
 void CompilerDriver::Verify(jobject jclass_loader,
                             const std::vector<const DexFile*>& dex_files,
                             TimingLogger* timings) {
@@ -2041,6 +2070,13 @@
           } else if (set.find(class_def.class_idx_) == set.end()) {
             ObjectLock<mirror::Class> lock(soa.Self(), cls);
             mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
+            // Create `VerifiedMethod`s for each methods, the compiler expects one for
+            // quickening or compiling.
+            // Note that this means:
+            // - We're only going to compile methods that did verify.
+            // - Quickening will not do checkcast ellision.
+            // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
+            PopulateVerifiedMethods(*dex_file, i, verification_results_);
           }
         }
       }
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 1ca439e..4b24ac3 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -63,9 +63,10 @@
   // We expected this for both core and fpu register pairs.
   return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
 }
-
+// Use a local definition to prevent copying mistakes.
+static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
+static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
 static constexpr int kCurrentMethodStackOffset = 0;
-static constexpr size_t kArmInstrMaxSizeInBytes = 4u;
 static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
 
 #ifdef __
@@ -438,6 +439,62 @@
   DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
 };
 
+class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
+ public:
+  explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
+      : SlowPathCodeARMVIXL(instruction) {}
+
+  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = instruction_->GetLocations();
+    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+    HLoadString* load = instruction_->AsLoadString();
+    const uint32_t string_index = load->GetStringIndex().index_;
+    vixl32::Register out = OutputRegister(load);
+    vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+    constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
+
+    CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
+    __ Bind(GetEntryLabel());
+    SaveLiveRegisters(codegen, locations);
+
+    InvokeRuntimeCallingConventionARMVIXL calling_convention;
+    // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
+    // the kSaveEverything call (or use `out` for the address after non-kSaveEverything call).
+    bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0)));
+    vixl32::Register entry_address = temp_is_r0 ? out : temp;
+    DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
+    if (call_saves_everything_except_r0 && temp_is_r0) {
+      __ Mov(entry_address, temp);
+    }
+
+    __ Mov(calling_convention.GetRegisterAt(0), string_index);
+    arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
+    CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
+
+    // Store the resolved String to the .bss entry.
+    if (call_saves_everything_except_r0) {
+      // The string entry address was preserved in `entry_address` thanks to kSaveEverything.
+      __ Str(r0, MemOperand(entry_address));
+    } else {
+      // For non-Baker read barrier, we need to re-calculate the address of the string entry.
+      CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+          arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
+      arm_codegen->EmitMovwMovtPlaceholder(labels, out);
+      __ Str(r0, MemOperand(entry_address));
+    }
+
+    arm_codegen->Move32(locations->Out(), LocationFrom(r0));
+    RestoreLiveRegisters(codegen, locations);
+
+    __ B(GetExitLabel());
+  }
+
+  const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARMVIXL"; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
+};
+
 class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
  public:
   TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
@@ -630,9 +687,30 @@
   return mask;
 }
 
-size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
-  GetAssembler()->LoadSFromOffset(vixl32::SRegister(reg_id), sp, stack_index);
-  return kArmWordSize;
+// Saves the register in the stack. Returns the size taken on stack.
+size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
+                                              uint32_t reg_id ATTRIBUTE_UNUSED) {
+  TODO_VIXL32(FATAL);
+  return 0;
+}
+
+// Restores the register from the stack. Returns the size taken on stack.
+size_t CodeGeneratorARMVIXL::RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
+                                                 uint32_t reg_id ATTRIBUTE_UNUSED) {
+  TODO_VIXL32(FATAL);
+  return 0;
+}
+
+size_t CodeGeneratorARMVIXL::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
+                                                       uint32_t reg_id ATTRIBUTE_UNUSED) {
+  TODO_VIXL32(FATAL);
+  return 0;
+}
+
+size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
+                                                          uint32_t reg_id ATTRIBUTE_UNUSED) {
+  TODO_VIXL32(FATAL);
+  return 0;
 }
 
 #undef __
@@ -655,7 +733,11 @@
       instruction_visitor_(graph, this),
       move_resolver_(graph->GetArena(), this),
       assembler_(graph->GetArena()),
-      isa_features_(isa_features) {
+      isa_features_(isa_features),
+      relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      pc_relative_type_patches_(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.
@@ -793,7 +875,7 @@
     __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
     // The load must immediately precede RecordPcInfo.
     AssemblerAccurateScope aas(GetVIXLAssembler(),
-                               kArmInstrMaxSizeInBytes,
+                               vixl32::kMaxInstructionSizeInBytes,
                                CodeBufferCheckScope::kMaximumSize);
     __ ldr(temp, MemOperand(temp));
     RecordPcInfo(nullptr, 0);
@@ -853,6 +935,116 @@
   __ Bind(GetLabelOf(block));
 }
 
+Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(Primitive::Type type) {
+  switch (type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      uint32_t index = gp_index_++;
+      uint32_t stack_index = stack_index_++;
+      if (index < calling_convention.GetNumberOfRegisters()) {
+        return LocationFrom(calling_convention.GetRegisterAt(index));
+      } else {
+        return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
+      }
+    }
+
+    case Primitive::kPrimLong: {
+      uint32_t index = gp_index_;
+      uint32_t stack_index = stack_index_;
+      gp_index_ += 2;
+      stack_index_ += 2;
+      if (index + 1 < calling_convention.GetNumberOfRegisters()) {
+        if (calling_convention.GetRegisterAt(index).Is(r1)) {
+          // Skip R1, and use R2_R3 instead.
+          gp_index_++;
+          index++;
+        }
+      }
+      if (index + 1 < calling_convention.GetNumberOfRegisters()) {
+        DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
+                  calling_convention.GetRegisterAt(index + 1).GetCode());
+
+        return LocationFrom(calling_convention.GetRegisterAt(index),
+                            calling_convention.GetRegisterAt(index + 1));
+      } else {
+        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
+      }
+    }
+
+    case Primitive::kPrimFloat: {
+      uint32_t stack_index = stack_index_++;
+      if (float_index_ % 2 == 0) {
+        float_index_ = std::max(double_index_, float_index_);
+      }
+      if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
+        return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
+      } else {
+        return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
+      }
+    }
+
+    case Primitive::kPrimDouble: {
+      double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
+      uint32_t stack_index = stack_index_;
+      stack_index_ += 2;
+      if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
+        uint32_t index = double_index_;
+        double_index_ += 2;
+        Location result = LocationFrom(
+          calling_convention.GetFpuRegisterAt(index),
+          calling_convention.GetFpuRegisterAt(index + 1));
+        DCHECK(ExpectedPairLayout(result));
+        return result;
+      } else {
+        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
+      }
+    }
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unexpected parameter type " << type;
+      break;
+  }
+  return Location::NoLocation();
+}
+
+Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(Primitive::Type type) const {
+  switch (type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      return LocationFrom(r0);
+    }
+
+    case Primitive::kPrimFloat: {
+      return LocationFrom(s0);
+    }
+
+    case Primitive::kPrimLong: {
+      return LocationFrom(r0, r1);
+    }
+
+    case Primitive::kPrimDouble: {
+      return LocationFrom(s0, s1);
+    }
+
+    case Primitive::kPrimVoid:
+      return Location::NoLocation();
+  }
+
+  UNREACHABLE();
+}
+
+Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
+  return LocationFrom(kMethodRegister);
+}
+
 void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
   if (source.Equals(destination)) {
     return;
@@ -924,10 +1116,14 @@
                                          uint32_t dex_pc,
                                          SlowPathCode* slow_path) {
   ValidateInvokeRuntime(entrypoint, instruction, slow_path);
-  GenerateInvokeRuntime(GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value());
+  __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value()));
+  // Ensure the pc position is recorded immediately after the `blx` instruction.
+  // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
+  AssemblerAccurateScope aas(GetVIXLAssembler(),
+                             vixl32::k16BitT32InstructionSizeInBytes,
+                             CodeBufferCheckScope::kExactSize);
+  __ blx(lr);
   if (EntrypointRequiresStackMap(entrypoint)) {
-    // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
-    // previous instruction.
     RecordPcInfo(instruction, dex_pc, slow_path);
   }
 }
@@ -936,11 +1132,7 @@
                                                                HInstruction* instruction,
                                                                SlowPathCode* slow_path) {
   ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
-  GenerateInvokeRuntime(entry_point_offset);
-}
-
-void CodeGeneratorARMVIXL::GenerateInvokeRuntime(int32_t entry_point_offset) {
-  GetAssembler()->LoadFromOffset(kLoadWord, lr, tr, entry_point_offset);
+  __ Ldr(lr, MemOperand(tr, entry_point_offset));
   __ Blx(lr);
 }
 
@@ -1270,6 +1462,19 @@
                         /* false_target */ nullptr);
 }
 
+void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+  LocationSummary* locations = new (GetGraph()->GetArena())
+      LocationSummary(flag, LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+  GetAssembler()->LoadFromOffset(kLoadWord,
+                                 OutputRegister(flag),
+                                 sp,
+                                 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
+}
+
 void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
   if (Primitive::IsFloatingPointType(select->GetType())) {
@@ -1360,7 +1565,7 @@
                CodeGenerator::GetInt32ValueOf(right.GetConstant()));
       }
       AssemblerAccurateScope aas(GetVIXLAssembler(),
-                                 kArmInstrMaxSizeInBytes * 3u,
+                                 3 * vixl32::kMaxInstructionSizeInBytes,
                                  CodeBufferCheckScope::kMaximumSize);
       __ ite(ARMCondition(cond->GetCondition()));
       __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
@@ -1575,7 +1780,10 @@
 
   HandleInvoke(invoke);
 
-  // TODO(VIXL): invoke->HasPcRelativeDexCache()
+  // For PC-relative dex cache the invoke has an extra input, the PC-relative address base.
+  if (invoke->HasPcRelativeDexCache()) {
+    invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::RequiresRegister());
+  }
 }
 
 static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
@@ -1597,15 +1805,13 @@
   }
 
   LocationSummary* locations = invoke->GetLocations();
-  DCHECK(locations->HasTemps());
-  codegen_->GenerateStaticOrDirectCall(invoke, locations->GetTemp(0));
-  // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
-  // previous instruction.
+  codegen_->GenerateStaticOrDirectCall(
+      invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
 
 void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
-  InvokeDexCallingConventionVisitorARM calling_convention_visitor;
+  InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
   CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
 }
 
@@ -1624,10 +1830,8 @@
   }
 
   codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
-  DCHECK(!codegen_->IsLeafMethod());
-  // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
-  // previous instruction.
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+  DCHECK(!codegen_->IsLeafMethod());
 }
 
 void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
@@ -1646,10 +1850,15 @@
 
   DCHECK(!receiver.IsStackSlot());
 
-  // /* HeapReference<Class> */ temp = receiver->klass_
-  GetAssembler()->LoadFromOffset(kLoadWord, temp, RegisterFrom(receiver), class_offset);
-
-  codegen_->MaybeRecordImplicitNullCheck(invoke);
+  // Ensure the pc position is recorded immediately after the `ldr` instruction.
+  {
+    AssemblerAccurateScope aas(GetVIXLAssembler(),
+                               vixl32::kMaxInstructionSizeInBytes,
+                               CodeBufferCheckScope::kMaximumSize);
+    // /* HeapReference<Class> */ temp = receiver->klass_
+    __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
+    codegen_->MaybeRecordImplicitNullCheck(invoke);
+  }
   // Instead of simply (possibly) unpoisoning `temp` here, we should
   // emit a read barrier for the previous class reference load.
   // However this is not required in practice, as this is an
@@ -1688,15 +1897,16 @@
     temps.Exclude(hidden_reg);
     __ Mov(hidden_reg, invoke->GetDexMethodIndex());
   }
-
   {
+    // Ensure the pc position is recorded immediately after the `blx` instruction.
+    // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
     AssemblerAccurateScope aas(GetVIXLAssembler(),
-                               kArmInstrMaxSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+                           vixl32::k16BitT32InstructionSizeInBytes,
+                           CodeBufferCheckScope::kExactSize);
     // LR();
     __ blx(lr);
-    DCHECK(!codegen_->IsLeafMethod());
     codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+    DCHECK(!codegen_->IsLeafMethod());
   }
 }
 
@@ -3067,7 +3277,7 @@
           __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
           {
             AssemblerAccurateScope guard(GetVIXLAssembler(),
-                                         3 * kArmInstrMaxSizeInBytes,
+                                         2 * vixl32::kMaxInstructionSizeInBytes,
                                          CodeBufferCheckScope::kMaximumSize);
             __ it(pl);
             __ lsl(pl, o_h, low, temp);
@@ -3086,7 +3296,7 @@
           __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
           {
             AssemblerAccurateScope guard(GetVIXLAssembler(),
-                                         3 * kArmInstrMaxSizeInBytes,
+                                         2 * vixl32::kMaxInstructionSizeInBytes,
                                          CodeBufferCheckScope::kMaximumSize);
             __ it(pl);
             __ asr(pl, o_l, high, temp);
@@ -3103,7 +3313,7 @@
           __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
           {
             AssemblerAccurateScope guard(GetVIXLAssembler(),
-                                         3 * kArmInstrMaxSizeInBytes,
+                                         2 * vixl32::kMaxInstructionSizeInBytes,
                                          CodeBufferCheckScope::kMaximumSize);
           __ it(pl);
           __ lsr(pl, o_l, high, temp);
@@ -3220,9 +3430,10 @@
     MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
     GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
     GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
+    // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
     AssemblerAccurateScope aas(GetVIXLAssembler(),
-                               kArmInstrMaxSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+                               vixl32::k16BitT32InstructionSizeInBytes,
+                               CodeBufferCheckScope::kExactSize);
     __ blx(lr);
     codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   } else {
@@ -3462,10 +3673,16 @@
     addr = temp;
   }
   __ Bind(&fail);
-  // We need a load followed by store. (The address used in a STREX instruction must
-  // be the same as the address in the most recently executed LDREX instruction.)
-  __ Ldrexd(temp1, temp2, MemOperand(addr));
-  codegen_->MaybeRecordImplicitNullCheck(instruction);
+  {
+    // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
+    AssemblerAccurateScope aas(GetVIXLAssembler(),
+                               vixl32::kMaxInstructionSizeInBytes,
+                               CodeBufferCheckScope::kMaximumSize);
+    // We need a load followed by store. (The address used in a STREX instruction must
+    // be the same as the address in the most recently executed LDREX instruction.)
+    __ ldrexd(temp1, temp2, MemOperand(addr));
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
   __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
   __ CompareAndBranchIfNonZero(temp1, &fail);
 }
@@ -3614,6 +3831,11 @@
 
   // Longs and doubles are handled in the switch.
   if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
+    // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
+    // should use a scope and the assembler to emit the store instruction to guarantee that we
+    // record the pc at the correct position. But the `Assembler` does not automatically handle
+    // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
+    // of writing, do generate the store instruction last.
     codegen_->MaybeRecordImplicitNullCheck(instruction);
   }
 
@@ -3788,7 +4010,6 @@
         TODO_VIXL32(FATAL);
       } else {
         GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
-        // TODO(VIXL): Scope to guarantee the position immediately after the load.
         codegen_->MaybeRecordImplicitNullCheck(instruction);
         if (is_volatile) {
           codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
@@ -3825,7 +4046,6 @@
         __ Vmov(out_dreg, lo, hi);
       } else {
         GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
-        // TODO(VIXL): Scope to guarantee the position immediately after the load.
         codegen_->MaybeRecordImplicitNullCheck(instruction);
       }
       break;
@@ -3841,6 +4061,11 @@
     // double fields, are handled in the previous switch statement.
   } else {
     // Address cases other than reference and double that may require an implicit null check.
+    // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
+    // should use a scope and the assembler to emit the load instruction to guarantee that we
+    // record the pc at the correct position. But the `Assembler` does not automatically handle
+    // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
+    // of writing, do generate the store instruction last.
     codegen_->MaybeRecordImplicitNullCheck(instruction);
   }
 
@@ -3965,8 +4190,9 @@
   }
 
   UseScratchRegisterScope temps(GetVIXLAssembler());
+  // Ensure the pc position is recorded immediately after the `ldr` instruction.
   AssemblerAccurateScope aas(GetVIXLAssembler(),
-                             kArmInstrMaxSizeInBytes,
+                             vixl32::kMaxInstructionSizeInBytes,
                              CodeBufferCheckScope::kMaximumSize);
   __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
   RecordPcInfo(instruction, instruction->GetDexPc());
@@ -4233,6 +4459,11 @@
           size_t offset =
               (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
           GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
+          // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
+          // we should use a scope and the assembler to emit the load instruction to guarantee that
+          // we record the pc at the correct position. But the `Assembler` does not automatically
+          // handle unencodable offsets. Practically, everything is fine because the helper and
+          // VIXL, at the time of writing, do generate the store instruction last.
           codegen_->MaybeRecordImplicitNullCheck(instruction);
           // If read barriers are enabled, emit read barriers other than
           // Baker's using a slow path (and also unpoison the loaded
@@ -4255,7 +4486,9 @@
           }
           codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
           temps.Release(temp);
-
+          // TODO(VIXL): Use a scope to ensure that we record the pc position immediately after the
+          // load instruction. Practically, everything is fine because the helper and VIXL, at the
+          // time of writing, do generate the store instruction last.
           codegen_->MaybeRecordImplicitNullCheck(instruction);
           // If read barriers are enabled, emit read barriers other than
           // Baker's using a slow path (and also unpoison the loaded
@@ -4317,6 +4550,8 @@
     // Potential implicit null checks, in the case of reference
     // arrays, are handled in the previous switch statement.
   } else if (!maybe_compressed_char_at) {
+    // TODO(VIXL): Use a scope to ensure we record the pc info immediately after
+    // the preceding load instruction.
     codegen_->MaybeRecordImplicitNullCheck(instruction);
   }
 }
@@ -4417,6 +4652,8 @@
           codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
           temps.Release(temp);
         }
+        // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
+        // store instruction.
         codegen_->MaybeRecordImplicitNullCheck(instruction);
         DCHECK(!needs_write_barrier);
         DCHECK(!may_need_runtime_call_for_type_check);
@@ -4451,6 +4688,8 @@
             codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
             temps.Release(temp);
           }
+          // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
+          // store instruction.
           codegen_->MaybeRecordImplicitNullCheck(instruction);
           __ B(&done);
           __ Bind(&non_zero);
@@ -4464,9 +4703,15 @@
         // negative, in which case we would take the ArraySet slow
         // path.
 
-        // /* HeapReference<Class> */ temp1 = array->klass_
-        GetAssembler()->LoadFromOffset(kLoadWord, temp1, array, class_offset);
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        {
+          // Ensure we record the pc position immediately after the `ldr` instruction.
+          AssemblerAccurateScope aas(GetVIXLAssembler(),
+                                     vixl32::kMaxInstructionSizeInBytes,
+                                     CodeBufferCheckScope::kMaximumSize);
+          // /* HeapReference<Class> */ temp1 = array->klass_
+          __ ldr(temp1, MemOperand(array, class_offset));
+          codegen_->MaybeRecordImplicitNullCheck(instruction);
+        }
         GetAssembler()->MaybeUnpoisonHeapReference(temp1);
 
         // /* HeapReference<Class> */ temp1 = temp1->component_type_
@@ -4523,6 +4768,8 @@
       }
 
       if (!may_need_runtime_call_for_type_check) {
+        // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
+        // instruction.
         codegen_->MaybeRecordImplicitNullCheck(instruction);
       }
 
@@ -4591,6 +4838,8 @@
 
   // Objects are handled in the switch.
   if (value_type != Primitive::kPrimNot) {
+    // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
+    // instruction.
     codegen_->MaybeRecordImplicitNullCheck(instruction);
   }
 }
@@ -4606,8 +4855,13 @@
   uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
   vixl32::Register obj = InputRegisterAt(instruction, 0);
   vixl32::Register out = OutputRegister(instruction);
-  GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
-  codegen_->MaybeRecordImplicitNullCheck(instruction);
+  {
+    AssemblerAccurateScope aas(GetVIXLAssembler(),
+                               vixl32::kMaxInstructionSizeInBytes,
+                               CodeBufferCheckScope::kMaximumSize);
+    __ ldr(out, MemOperand(obj, offset));
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
   // Mask out compression flag from String's array length.
   if (mirror::kUseStringCompression && instruction->IsStringLength()) {
     __ Lsr(out, out, 1u);
@@ -4985,12 +5239,37 @@
   TODO_VIXL32(FATAL);
 }
 
-// Check if the desired_class_load_kind is supported. If it is, return it,
-// otherwise return a fall-back kind that should be used instead.
 HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
-      HLoadClass::LoadKind desired_class_load_kind ATTRIBUTE_UNUSED) {
-  // TODO(VIXL): Implement optimized code paths.
-  return HLoadClass::LoadKind::kDexCacheViaMethod;
+    HLoadClass::LoadKind desired_class_load_kind) {
+  switch (desired_class_load_kind) {
+    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;
+    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;
+    case HLoadClass::LoadKind::kDexCacheAddress:
+      // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
+      return HLoadClass::LoadKind::kDexCacheViaMethod;
+    case HLoadClass::LoadKind::kDexCachePcRelative:
+      DCHECK(!Runtime::Current()->UseJitCompilation());
+      // We disable pc-relative load when there is an irreducible loop, as the optimization
+      // is incompatible with it.
+      // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
+      // with irreducible loops.
+      if (GetGraph()->HasIrreducibleLoops()) {
+        return HLoadClass::LoadKind::kDexCacheViaMethod;
+      }
+      break;
+    case HLoadClass::LoadKind::kDexCacheViaMethod:
+      break;
+  }
+  return desired_class_load_kind;
 }
 
 void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
@@ -5004,11 +5283,15 @@
     return;
   }
 
-  // TODO(VIXL): read barrier code.
-  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
       ? LocationSummary::kCallOnSlowPath
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+  if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
+      TODO_VIXL32(FATAL);
+  }
+
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
       load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
@@ -5030,7 +5313,9 @@
   Location out_loc = locations->Out();
   vixl32::Register out = OutputRegister(cls);
 
-  // TODO(VIXL): read barrier code.
+  const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
+      ? kWithoutReadBarrier
+      : kCompilerReadBarrierOption;
   bool generate_null_check = false;
   switch (cls->GetLoadKind()) {
     case HLoadClass::LoadKind::kReferrersClass: {
@@ -5042,7 +5327,35 @@
                               out_loc,
                               current_method,
                               ArtMethod::DeclaringClassOffset().Int32Value(),
-                              kEmitCompilerReadBarrier);
+                              read_barrier_option);
+      break;
+    }
+    case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
+      TODO_VIXL32(FATAL);
+      break;
+    }
+    case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
+      DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
+      CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+          codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+      codegen_->EmitMovwMovtPlaceholder(labels, out);
+      break;
+    }
+    case HLoadClass::LoadKind::kBootImageAddress: {
+      TODO_VIXL32(FATAL);
+      break;
+    }
+    case HLoadClass::LoadKind::kDexCacheAddress: {
+      TODO_VIXL32(FATAL);
+      break;
+    }
+    case HLoadClass::LoadKind::kDexCachePcRelative: {
+      vixl32::Register base_reg = InputRegisterAt(cls, 0);
+      HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase();
+      int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset();
+      // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset)
+      GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset, read_barrier_option);
+      generate_null_check = !cls->IsInDexCache();
       break;
     }
     case HLoadClass::LoadKind::kDexCacheViaMethod: {
@@ -5054,7 +5367,7 @@
       GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset);
       // /* GcRoot<mirror::Class> */ out = out[type_index]
       size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
-      GenerateGcRootFieldLoad(cls, out_loc, out, offset, kEmitCompilerReadBarrier);
+      GenerateGcRootFieldLoad(cls, out_loc, out, offset, read_barrier_option);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -5114,37 +5427,101 @@
   __ Bind(slow_path->GetExitLabel());
 }
 
-// Check if the desired_string_load_kind is supported. If it is, return it,
-// otherwise return a fall-back kind that should be used instead.
 HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
-      HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
-  // TODO(VIXL): Implement optimized code paths. For now we always use the simpler fallback code.
-  return HLoadString::LoadKind::kDexCacheViaMethod;
+    HLoadString::LoadKind desired_string_load_kind) {
+  switch (desired_string_load_kind) {
+    case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+      // TODO(VIXL): Implement missing optimization.
+      return HLoadString::LoadKind::kDexCacheViaMethod;
+    case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+      DCHECK(GetCompilerOptions().GetCompilePic());
+      break;
+    case HLoadString::LoadKind::kBootImageAddress:
+      // TODO(VIXL): Implement missing optimization.
+      return HLoadString::LoadKind::kDexCacheViaMethod;
+    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;
+    case HLoadString::LoadKind::kDexCacheViaMethod:
+      break;
+  }
+  return desired_string_load_kind;
 }
 
 void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = load->NeedsEnvironment()
-      ? LocationSummary::kCallOnMainOnly
-      : LocationSummary::kNoCall;
+  LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
-
-  // TODO(VIXL): Implement optimized code paths.
-  // See InstructionCodeGeneratorARMVIXL::VisitLoadString.
   HLoadString::LoadKind load_kind = load->GetLoadKind();
   if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
-    locations->SetInAt(0, Location::RequiresRegister());
-    // TODO(VIXL): Use InvokeRuntimeCallingConventionARMVIXL instead.
     locations->SetOut(LocationFrom(r0));
   } else {
     locations->SetOut(Location::RequiresRegister());
+    if (load_kind == HLoadString::LoadKind::kBssEntry) {
+      if (!kUseReadBarrier || kUseBakerReadBarrier) {
+        // Rely on the pResolveString and/or marking to save everything, including temps.
+        // Note that IP may theoretically be clobbered by saving/restoring the live register
+        // (only one thanks to the custom calling convention), so we request a different temp.
+        locations->AddTemp(Location::RequiresRegister());
+        RegisterSet caller_saves = RegisterSet::Empty();
+        InvokeRuntimeCallingConventionARMVIXL calling_convention;
+        caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
+        // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
+        // that the the kPrimNot result register is the same as the first argument register.
+        locations->SetCustomSlowPathCallerSaves(caller_saves);
+      } else {
+        // For non-Baker read barrier we have a temp-clobbering call.
+      }
+    }
   }
 }
 
 void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) {
-  // TODO(VIXL): Implement optimized code paths.
-  // We implemented the simplest solution to get first ART tests passing, we deferred the
-  // optimized path until later, we should implement it using ARM64 implementation as a
-  // reference. The same related to LocationsBuilderARMVIXL::VisitLoadString.
+  LocationSummary* locations = load->GetLocations();
+  Location out_loc = locations->Out();
+  vixl32::Register out = OutputRegister(load);
+  HLoadString::LoadKind load_kind = load->GetLoadKind();
+
+  switch (load_kind) {
+    case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
+      TODO_VIXL32(FATAL);
+      break;
+    }
+    case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+      DCHECK(codegen_->GetCompilerOptions().IsBootImage());
+      CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+          codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
+      codegen_->EmitMovwMovtPlaceholder(labels, out);
+      return;  // No dex cache slow path.
+    }
+    case HLoadString::LoadKind::kBootImageAddress: {
+      TODO_VIXL32(FATAL);
+      break;
+    }
+    case HLoadString::LoadKind::kBssEntry: {
+      DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+      CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+          codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
+      codegen_->EmitMovwMovtPlaceholder(labels, temp);
+      GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption);
+      LoadStringSlowPathARMVIXL* slow_path =
+          new (GetGraph()->GetArena()) LoadStringSlowPathARMVIXL(load);
+      codegen_->AddSlowPath(slow_path);
+      __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
+      __ Bind(slow_path->GetExitLabel());
+      return;
+    }
+    case HLoadString::LoadKind::kJitTableAddress: {
+      TODO_VIXL32(FATAL);
+      break;
+    }
+    default:
+      break;
+  }
 
   // TODO: Re-add the compiler code to do string dex cache lookup again.
   DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
@@ -5999,9 +6376,9 @@
     Location root,
     vixl32::Register obj,
     uint32_t offset,
-    bool requires_read_barrier) {
+    ReadBarrierOption read_barrier_option) {
   vixl32::Register root_reg = RegisterFrom(root);
-  if (requires_read_barrier) {
+  if (read_barrier_option == kWithReadBarrier) {
     TODO_VIXL32(FATAL);
   } else {
     // Plain GC root load with no read barrier.
@@ -6062,15 +6439,51 @@
 // Check if the desired_dispatch_info is supported. If it is, return it,
 // otherwise return a fall-back info that should be used instead.
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
-      const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info ATTRIBUTE_UNUSED,
-      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
+    const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+    HInvokeStaticOrDirect* invoke) {
   // TODO(VIXL): Implement optimized code paths.
-  return {
-    HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
-    HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-    0u,
-    0u
-  };
+  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.
+  // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
+  // with irreducible loops.
+  if (GetGraph()->HasIrreducibleLoops() &&
+      (dispatch_info.method_load_kind ==
+          HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
+    dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
+  }
+
+  if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) {
+    const DexFile& outer_dex_file = GetGraph()->GetDexFile();
+    if (&outer_dex_file != invoke->GetTargetMethod().dex_file) {
+      // Calls across dex files are more likely to exceed the available BL range,
+      // so use absolute patch with fixup if available and kCallArtMethod otherwise.
+      HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
+          (desired_dispatch_info.method_load_kind ==
+           HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup)
+          ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup
+          : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
+      return HInvokeStaticOrDirect::DispatchInfo {
+        dispatch_info.method_load_kind,
+        code_ptr_location,
+        dispatch_info.method_load_data,
+        0u
+      };
+    }
+  }
+  return dispatch_info;
 }
 
 vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
@@ -6101,59 +6514,119 @@
 
 void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
     HInvokeStaticOrDirect* invoke, Location temp) {
-  Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
-  vixl32::Register temp_reg = RegisterFrom(temp);
+  // For better instruction scheduling we load the direct code pointer before the method pointer.
+  switch (invoke->GetCodePtrLocation()) {
+    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+      // LR = code address from literal pool with link-time patch.
+      TODO_VIXL32(FATAL);
+      break;
+    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
+      // LR = invoke->GetDirectCodePtr();
+      __ Mov(lr, Operand::From(invoke->GetDirectCodePtr()));
+      break;
+    default:
+      break;
+  }
 
+  Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
   switch (invoke->GetMethodLoadKind()) {
     case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
       uint32_t offset =
           GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
       // temp = thread->string_init_entrypoint
-      GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, tr, offset);
+      GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
+      break;
+    }
+    case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
+      callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
+      break;
+    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
+      __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
+      break;
+    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
+      TODO_VIXL32(FATAL);
+      break;
+    case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
+      HArmDexCacheArraysBase* base =
+          invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase();
+      vixl32::Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, RegisterFrom(temp));
+      int32_t offset = invoke->GetDexCacheArrayOffset() - base->GetElementOffset();
+      GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), base_reg, offset);
       break;
     }
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
       Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
       vixl32::Register method_reg;
+      vixl32::Register reg = RegisterFrom(temp);
       if (current_method.IsRegister()) {
         method_reg = RegisterFrom(current_method);
       } else {
         DCHECK(invoke->GetLocations()->Intrinsified());
         DCHECK(!current_method.IsValid());
-        method_reg = temp_reg;
-        GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, sp, kCurrentMethodStackOffset);
+        method_reg = reg;
+        GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, kCurrentMethodStackOffset);
       }
       // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_;
       GetAssembler()->LoadFromOffset(
           kLoadWord,
-          temp_reg,
+          reg,
           method_reg,
           ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value());
       // temp = temp[index_in_cache];
       // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
       uint32_t index_in_cache = invoke->GetDexMethodIndex();
       GetAssembler()->LoadFromOffset(
-          kLoadWord, temp_reg, temp_reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
+          kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
       break;
     }
-    default:
-      TODO_VIXL32(FATAL);
   }
 
-  // TODO(VIXL): Support `CodePtrLocation` values other than `kCallArtMethod`.
-  if (invoke->GetCodePtrLocation() != HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod) {
-    TODO_VIXL32(FATAL);
+  switch (invoke->GetCodePtrLocation()) {
+    case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
+      __ Bl(GetFrameEntryLabel());
+      break;
+    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
+      relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
+                                          invoke->GetTargetMethod().dex_method_index);
+      {
+        AssemblerAccurateScope aas(GetVIXLAssembler(),
+                                   vixl32::kMaxInstructionSizeInBytes,
+                                   CodeBufferCheckScope::kMaximumSize);
+        __ bind(&relative_call_patches_.back().label);
+        // Arbitrarily branch to the BL itself, override at link time.
+        __ bl(&relative_call_patches_.back().label);
+      }
+      break;
+    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
+      // LR prepared above for better instruction scheduling.
+      // LR()
+      {
+        // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
+        AssemblerAccurateScope aas(GetVIXLAssembler(),
+                                   vixl32::k16BitT32InstructionSizeInBytes,
+                                   CodeBufferCheckScope::kExactSize);
+        __ blx(lr);
+      }
+      break;
+    case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
+      // LR = callee_method->entry_point_from_quick_compiled_code_
+      GetAssembler()->LoadFromOffset(
+            kLoadWord,
+            lr,
+            RegisterFrom(callee_method),
+            ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
+      {
+        // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
+        AssemblerAccurateScope aas(GetVIXLAssembler(),
+                                   vixl32::k16BitT32InstructionSizeInBytes,
+                                   CodeBufferCheckScope::kExactSize);
+        // LR()
+        __ blx(lr);
+      }
+      break;
   }
 
-  // LR = callee_method->entry_point_from_quick_compiled_code_
-  GetAssembler()->LoadFromOffset(
-      kLoadWord,
-      lr,
-      RegisterFrom(callee_method),
-      ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
-  // LR()
-  __ Blx(lr);
-
   DCHECK(!IsLeafMethod());
 }
 
@@ -6169,9 +6642,15 @@
   InvokeDexCallingConventionARMVIXL calling_convention;
   vixl32::Register receiver = calling_convention.GetRegisterAt(0);
   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  // /* HeapReference<Class> */ temp = receiver->klass_
-  GetAssembler()->LoadFromOffset(kLoadWord, temp, receiver, class_offset);
-  MaybeRecordImplicitNullCheck(invoke);
+  {
+    // Make sure the pc is recorded immediately after the `ldr` instruction.
+    AssemblerAccurateScope aas(GetVIXLAssembler(),
+                               vixl32::kMaxInstructionSizeInBytes,
+                               CodeBufferCheckScope::kMaximumSize);
+    // /* HeapReference<Class> */ temp = receiver->klass_
+    __ ldr(temp, MemOperand(receiver, class_offset));
+    MaybeRecordImplicitNullCheck(invoke);
+  }
   // Instead of simply (possibly) unpoisoning `temp` here, we should
   // emit a read barrier for the previous class reference load.
   // However this is not required in practice, as this is an
@@ -6188,7 +6667,81 @@
   // LR = temp->GetEntryPoint();
   GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
   // LR();
-  __ Blx(lr);
+  // This `blx` *must* be the *last* instruction generated by this stub, so that calls to
+  // `RecordPcInfo()` immediately following record the correct pc. Use a scope to help guarantee
+  // that.
+  // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
+  AssemblerAccurateScope aas(GetVIXLAssembler(),
+                             vixl32::k16BitT32InstructionSizeInBytes,
+                             CodeBufferCheckScope::kExactSize);
+  __ blx(lr);
+}
+
+CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeStringPatch(
+    const DexFile& dex_file, uint32_t string_index) {
+  return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_);
+}
+
+CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeTypePatch(
+    const DexFile& dex_file, dex::TypeIndex type_index) {
+  return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
+}
+
+CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeDexCacheArrayPatch(
+    const DexFile& dex_file, uint32_t element_offset) {
+  return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
+}
+
+CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
+    const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
+  patches->emplace_back(dex_file, offset_or_index);
+  return &patches->back();
+}
+
+template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
+inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
+    const ArenaDeque<PcRelativePatchInfo>& infos,
+    ArenaVector<LinkerPatch>* linker_patches) {
+  for (const PcRelativePatchInfo& info : infos) {
+    const DexFile& dex_file = info.target_dex_file;
+    size_t offset_or_index = info.offset_or_index;
+    DCHECK(info.add_pc_label.IsBound());
+    uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
+    // Add MOVW patch.
+    DCHECK(info.movw_label.IsBound());
+    uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
+    linker_patches->push_back(Factory(movw_offset, &dex_file, add_pc_offset, offset_or_index));
+    // Add MOVT patch.
+    DCHECK(info.movt_label.IsBound());
+    uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
+    linker_patches->push_back(Factory(movt_offset, &dex_file, add_pc_offset, offset_or_index));
+  }
+}
+
+void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
+  DCHECK(linker_patches->empty());
+  size_t size =
+      relative_call_patches_.size() +
+      /* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() +
+      /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
+      /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size();
+  linker_patches->reserve(size);
+  for (const PatchInfo<vixl32::Label>& info : relative_call_patches_) {
+    uint32_t literal_offset = info.label.GetLocation();
+    linker_patches->push_back(
+        LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
+  }
+  EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
+                                                               linker_patches);
+  if (!GetCompilerOptions().IsBootImage()) {
+    EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
+                                                                  linker_patches);
+  } else {
+    EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
+                                                                  linker_patches);
+  }
+  EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
+                                                              linker_patches);
 }
 
 void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
@@ -6315,6 +6868,17 @@
     jump_table->EmitTable(codegen_);
   }
 }
+void LocationsBuilderARMVIXL::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
+  vixl32::Register base_reg = OutputRegister(base);
+  CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+      codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset());
+  codegen_->EmitMovwMovtPlaceholder(labels, base_reg);
+}
 
 // Copy the result of a call into the given target.
 void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type type) {
@@ -6325,7 +6889,7 @@
 
   DCHECK_NE(type, Primitive::kPrimVoid);
 
-  Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type);
+  Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
   if (return_loc.Equals(trg)) {
     return;
   }
@@ -6373,6 +6937,21 @@
   }
 }
 
+void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
+    CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
+    vixl32::Register out) {
+  AssemblerAccurateScope aas(GetVIXLAssembler(),
+                             3 * vixl32::kMaxInstructionSizeInBytes,
+                             CodeBufferCheckScope::kMaximumSize);
+  // TODO(VIXL): Think about using mov instead of movw.
+  __ bind(&labels->movw_label);
+  __ movw(out, /* placeholder */ 0u);
+  __ bind(&labels->movt_label);
+  __ movt(out, /* placeholder */ 0u);
+  __ bind(&labels->add_pc_label);
+  __ add(out, out, pc);
+}
+
 #undef __
 #undef QUICK_ENTRY_POINT
 #undef TODO_VIXL32
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index bd91127..b7ba8dd 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -17,9 +17,15 @@
 #ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_VIXL_H_
 #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_VIXL_H_
 
-#include "code_generator_arm.h"
+#include "base/enums.h"
+#include "code_generator.h"
 #include "common_arm.h"
+#include "driver/compiler_options.h"
+#include "nodes.h"
+#include "string_reference.h"
+#include "parallel_move_resolver.h"
 #include "utils/arm/assembler_arm_vixl.h"
+#include "utils/type_reference.h"
 
 // TODO(VIXL): make vixl clean wrt -Wshadow.
 #pragma GCC diagnostic push
@@ -44,7 +50,7 @@
     vixl::aarch32::r2,
     vixl::aarch32::r3
 };
-static const size_t kParameterCoreRegistersLengthVIXL = arraysize(kParameterCoreRegisters);
+static const size_t kParameterCoreRegistersLengthVIXL = arraysize(kParameterCoreRegistersVIXL);
 static const vixl::aarch32::SRegister kParameterFpuRegistersVIXL[] = {
     vixl::aarch32::s0,
     vixl::aarch32::s1,
@@ -63,7 +69,7 @@
     vixl::aarch32::s14,
     vixl::aarch32::s15
 };
-static const size_t kParameterFpuRegistersLengthVIXL = arraysize(kParameterFpuRegisters);
+static const size_t kParameterFpuRegistersLengthVIXL = arraysize(kParameterFpuRegistersVIXL);
 
 static const vixl::aarch32::Register kMethodRegister = vixl::aarch32::r0;
 
@@ -90,7 +96,7 @@
     vixl::aarch32::r3
 };
 static const size_t kRuntimeParameterCoreRegistersLengthVIXL =
-    arraysize(kRuntimeParameterCoreRegisters);
+    arraysize(kRuntimeParameterCoreRegistersVIXL);
 static const vixl::aarch32::SRegister kRuntimeParameterFpuRegistersVIXL[] = {
     vixl::aarch32::s0,
     vixl::aarch32::s1,
@@ -98,98 +104,10 @@
     vixl::aarch32::s3
 };
 static const size_t kRuntimeParameterFpuRegistersLengthVIXL =
-    arraysize(kRuntimeParameterFpuRegisters);
+    arraysize(kRuntimeParameterFpuRegistersVIXL);
 
 class LoadClassSlowPathARMVIXL;
 
-#define FOR_EACH_IMPLEMENTED_INSTRUCTION(M)     \
-  M(Above)                                      \
-  M(AboveOrEqual)                               \
-  M(Add)                                        \
-  M(And)                                        \
-  M(ArrayGet)                                   \
-  M(ArrayLength)                                \
-  M(ArraySet)                                   \
-  M(Below)                                      \
-  M(BelowOrEqual)                               \
-  M(BitwiseNegatedRight)                        \
-  M(BooleanNot)                                 \
-  M(BoundsCheck)                                \
-  M(BoundType)                                  \
-  M(CheckCast)                                  \
-  M(ClassTableGet)                              \
-  M(ClearException)                             \
-  M(ClinitCheck)                                \
-  M(Compare)                                    \
-  M(CurrentMethod)                              \
-  M(Deoptimize)                                 \
-  M(Div)                                        \
-  M(DivZeroCheck)                               \
-  M(DoubleConstant)                             \
-  M(Equal)                                      \
-  M(Exit)                                       \
-  M(FloatConstant)                              \
-  M(Goto)                                       \
-  M(GreaterThan)                                \
-  M(GreaterThanOrEqual)                         \
-  M(If)                                         \
-  M(InstanceFieldGet)                           \
-  M(InstanceFieldSet)                           \
-  M(InstanceOf)                                 \
-  M(IntConstant)                                \
-  M(IntermediateAddress)                        \
-  M(InvokeInterface)                            \
-  M(InvokeStaticOrDirect)                       \
-  M(InvokeUnresolved)                           \
-  M(InvokeVirtual)                              \
-  M(LessThan)                                   \
-  M(LessThanOrEqual)                            \
-  M(LoadClass)                                  \
-  M(LoadException)                              \
-  M(LoadString)                                 \
-  M(LongConstant)                               \
-  M(MemoryBarrier)                              \
-  M(MonitorOperation)                           \
-  M(Mul)                                        \
-  M(MultiplyAccumulate)                         \
-  M(NativeDebugInfo)                            \
-  M(Neg)                                        \
-  M(NewArray)                                   \
-  M(NewInstance)                                \
-  M(Not)                                        \
-  M(NotEqual)                                   \
-  M(NullCheck)                                  \
-  M(NullConstant)                               \
-  M(Or)                                         \
-  M(PackedSwitch)                               \
-  M(ParallelMove)                               \
-  M(ParameterValue)                             \
-  M(Phi)                                        \
-  M(Rem)                                        \
-  M(Return)                                     \
-  M(ReturnVoid)                                 \
-  M(Ror)                                        \
-  M(Select)                                     \
-  M(Shl)                                        \
-  M(Shr)                                        \
-  M(StaticFieldGet)                             \
-  M(StaticFieldSet)                             \
-  M(Sub)                                        \
-  M(SuspendCheck)                               \
-  M(Throw)                                      \
-  M(TryBoundary)                                \
-  M(TypeConversion)                             \
-  M(UnresolvedInstanceFieldGet)                 \
-  M(UnresolvedInstanceFieldSet)                 \
-  M(UnresolvedStaticFieldGet)                   \
-  M(UnresolvedStaticFieldSet)                   \
-  M(UShr)                                       \
-  M(Xor)                                        \
-
-// TODO: Remove once the VIXL32 backend is implemented completely.
-#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M)   \
-  M(ArmDexCacheArraysBase)                      \
-
 class CodeGeneratorARMVIXL;
 
 class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> {
@@ -248,6 +166,22 @@
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionARMVIXL);
 };
 
+class InvokeDexCallingConventionVisitorARMVIXL : public InvokeDexCallingConventionVisitor {
+ public:
+  InvokeDexCallingConventionVisitorARMVIXL() {}
+  virtual ~InvokeDexCallingConventionVisitorARMVIXL() {}
+
+  Location GetNextLocation(Primitive::Type type) OVERRIDE;
+  Location GetReturnLocation(Primitive::Type type) const OVERRIDE;
+  Location GetMethodLocation() const OVERRIDE;
+
+ private:
+  InvokeDexCallingConventionARMVIXL calling_convention;
+  uint32_t double_index_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorARMVIXL);
+};
+
 class FieldAccessCallingConventionARMVIXL : public FieldAccessCallingConvention {
  public:
   FieldAccessCallingConventionARMVIXL() {}
@@ -319,27 +253,26 @@
   DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverARMVIXL);
 };
 
-#define DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR(Name)            \
-  void Visit##Name(H##Name*) OVERRIDE;
-
-#define DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR(Name)          \
-  void Visit##Name(H##Name* instr) OVERRIDE {                   \
-    VisitUnimplemementedInstruction(instr); }
-
 class LocationsBuilderARMVIXL : public HGraphVisitor {
  public:
   LocationsBuilderARMVIXL(HGraph* graph, CodeGeneratorARMVIXL* codegen)
       : HGraphVisitor(graph), codegen_(codegen) {}
 
-  FOR_EACH_IMPLEMENTED_INSTRUCTION(DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR)
+#define DECLARE_VISIT_INSTRUCTION(name, super)     \
+  void Visit##name(H##name* instr) OVERRIDE;
 
-  FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR)
+  FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
 
- private:
-  void VisitUnimplemementedInstruction(HInstruction* instruction) {
-    LOG(FATAL) << "Unimplemented Instruction: " << instruction->DebugName();
+#undef DECLARE_VISIT_INSTRUCTION
+
+  void VisitInstruction(HInstruction* instruction) OVERRIDE {
+    LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+               << " (id " << instruction->GetId() << ")";
   }
 
+ private:
   void HandleInvoke(HInvoke* invoke);
   void HandleBitwiseOperation(HBinaryOperation* operation, Opcode opcode);
   void HandleCondition(HCondition* condition);
@@ -355,7 +288,7 @@
   bool CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode, SetCc set_cc = kCcDontCare);
 
   CodeGeneratorARMVIXL* const codegen_;
-  InvokeDexCallingConventionVisitorARM parameter_visitor_;
+  InvokeDexCallingConventionVisitorARMVIXL parameter_visitor_;
 
   DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARMVIXL);
 };
@@ -364,25 +297,30 @@
  public:
   InstructionCodeGeneratorARMVIXL(HGraph* graph, CodeGeneratorARMVIXL* codegen);
 
-  FOR_EACH_IMPLEMENTED_INSTRUCTION(DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR)
+#define DECLARE_VISIT_INSTRUCTION(name, super)     \
+  void Visit##name(H##name* instr) OVERRIDE;
 
-  FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR)
+  FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
+
+#undef DECLARE_VISIT_INSTRUCTION
+
+  void VisitInstruction(HInstruction* instruction) OVERRIDE {
+    LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+               << " (id " << instruction->GetId() << ")";
+  }
 
   ArmVIXLAssembler* GetAssembler() const { return assembler_; }
   ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
 
  private:
-  void VisitUnimplemementedInstruction(HInstruction* instruction) {
-    LOG(FATAL) << "Unimplemented Instruction: " << instruction->DebugName();
-  }
-
   // Generate code for the given suspend check. If not null, `successor`
   // is the block to branch to if the suspend check is not needed, and after
   // the suspend call.
   void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
   void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path,
                                         vixl32::Register class_reg);
-  void HandleGoto(HInstruction* got, HBasicBlock* successor);
   void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
   void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
   void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
@@ -440,17 +378,16 @@
                                          uint32_t offset,
                                          Location maybe_temp,
                                          ReadBarrierOption read_barrier_option);
-
   // Generate a GC root reference load:
   //
   //   root <- *(obj + offset)
   //
-  // while honoring read barriers if `requires_read_barrier` is true.
+  // while honoring read barriers based on read_barrier_option.
   void GenerateGcRootFieldLoad(HInstruction* instruction,
                                Location root,
                                vixl::aarch32::Register obj,
                                uint32_t offset,
-                               bool requires_read_barrier);
+                               ReadBarrierOption read_barrier_option);
   void GenerateTestAndBranch(HInstruction* instruction,
                              size_t condition_input_index,
                              vixl::aarch32::Label* true_target,
@@ -470,6 +407,7 @@
   void DivRemByPowerOfTwo(HBinaryOperation* instruction);
   void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
   void GenerateDivRemConstantIntegral(HBinaryOperation* instruction);
+  void HandleGoto(HInstruction* got, HBasicBlock* successor);
 
   ArmVIXLAssembler* const assembler_;
   CodeGeneratorARMVIXL* const codegen_;
@@ -483,62 +421,50 @@
                        const ArmInstructionSetFeatures& isa_features,
                        const CompilerOptions& compiler_options,
                        OptimizingCompilerStats* stats = nullptr);
-
   virtual ~CodeGeneratorARMVIXL() {}
 
-  void Initialize() OVERRIDE {
-    block_labels_.resize(GetGraph()->GetBlocks().size());
-  }
-
   void GenerateFrameEntry() OVERRIDE;
   void GenerateFrameExit() OVERRIDE;
-
   void Bind(HBasicBlock* block) OVERRIDE;
-
-  vixl::aarch32::Label* GetLabelOf(HBasicBlock* block) {
-    block = FirstNonEmptyBlock(block);
-    return &(block_labels_[block->GetBlockId()]);
-  }
-
   void MoveConstant(Location destination, int32_t value) OVERRIDE;
   void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
   void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
 
+  size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+
+  size_t GetWordSize() const OVERRIDE {
+    return static_cast<size_t>(kArmPointerSize);
+  }
+
+  size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return vixl::aarch32::kRegSizeInBytes; }
+
+  HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; }
+
+  HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
+
   ArmVIXLAssembler* GetAssembler() OVERRIDE { return &assembler_; }
 
   const ArmVIXLAssembler& GetAssembler() const OVERRIDE { return assembler_; }
 
   ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
 
-  size_t GetWordSize() const OVERRIDE { return kArmWordSize; }
-
-  size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return vixl::aarch32::kRegSizeInBytes; }
-
   uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
     vixl::aarch32::Label* block_entry_label = GetLabelOf(block);
     DCHECK(block_entry_label->IsBound());
     return block_entry_label->GetLocation();
   }
 
-  JumpTableARMVIXL* CreateJumpTable(HPackedSwitch* switch_instr) {
-    jump_tables_.emplace_back(new (GetGraph()->GetArena()) JumpTableARMVIXL(switch_instr));
-    return jump_tables_.back().get();
-  }
-
-  HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; }
-
-  HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
-
   void FixJumpTables();
-  void GenerateMemoryBarrier(MemBarrierKind kind);
-  void Finalize(CodeAllocator* allocator) OVERRIDE;
   void SetupBlockedRegisters() const OVERRIDE;
 
   void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
   void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
 
+  ParallelMoveResolver* GetMoveResolver() OVERRIDE { return &move_resolver_; }
   InstructionSet GetInstructionSet() const OVERRIDE { return InstructionSet::kThumb2; }
-
   // Helper method to move a 32-bit value between two locations.
   void Move32(Location destination, Location source);
 
@@ -553,45 +479,6 @@
                                vixl::aarch32::Register reg_index,
                                vixl::aarch32::Condition cond = vixl::aarch32::al);
 
-  const ArmInstructionSetFeatures& GetInstructionSetFeatures() const { return isa_features_; }
-
-  vixl::aarch32::Label* GetFrameEntryLabel() { return &frame_entry_label_; }
-
-  // Saves the register in the stack. Returns the size taken on stack.
-  size_t SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
-                          uint32_t reg_id ATTRIBUTE_UNUSED) OVERRIDE {
-    UNIMPLEMENTED(INFO) << "TODO: SaveCoreRegister";
-    return 0;
-  }
-
-  // Restores the register from the stack. Returns the size taken on stack.
-  size_t RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
-                             uint32_t reg_id ATTRIBUTE_UNUSED) OVERRIDE {
-    UNIMPLEMENTED(INFO) << "TODO: RestoreCoreRegister";
-    return 0;
-  }
-
-  size_t SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
-                                   uint32_t reg_id ATTRIBUTE_UNUSED) OVERRIDE {
-    UNIMPLEMENTED(INFO) << "TODO: SaveFloatingPointRegister";
-    return 0;
-  }
-
-  size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
-
-  bool NeedsTwoRegisters(Primitive::Type type) const OVERRIDE {
-    return type == Primitive::kPrimDouble || type == Primitive::kPrimLong;
-  }
-
-  void ComputeSpillMask() OVERRIDE;
-
-  void GenerateImplicitNullCheck(HNullCheck* null_check) OVERRIDE;
-  void GenerateExplicitNullCheck(HNullCheck* null_check) OVERRIDE;
-
-  ParallelMoveResolver* GetMoveResolver() OVERRIDE {
-    return &move_resolver_;
-  }
-
   // Generate code to invoke a runtime entry point.
   void InvokeRuntime(QuickEntrypointEnum entrypoint,
                      HInstruction* instruction,
@@ -604,8 +491,6 @@
                                            HInstruction* instruction,
                                            SlowPathCode* slow_path);
 
-  void GenerateInvokeRuntime(int32_t entry_point_offset);
-
   // Emit a write barrier.
   void MarkGCCard(vixl::aarch32::Register temp,
                   vixl::aarch32::Register card,
@@ -613,6 +498,76 @@
                   vixl::aarch32::Register value,
                   bool can_be_null);
 
+  void GenerateMemoryBarrier(MemBarrierKind kind);
+
+  vixl::aarch32::Label* GetLabelOf(HBasicBlock* block) {
+    block = FirstNonEmptyBlock(block);
+    return &(block_labels_[block->GetBlockId()]);
+  }
+
+  void Initialize() OVERRIDE {
+    block_labels_.resize(GetGraph()->GetBlocks().size());
+  }
+
+  void Finalize(CodeAllocator* allocator) OVERRIDE;
+
+  const ArmInstructionSetFeatures& GetInstructionSetFeatures() const { return isa_features_; }
+
+  bool NeedsTwoRegisters(Primitive::Type type) const OVERRIDE {
+    return type == Primitive::kPrimDouble || type == Primitive::kPrimLong;
+  }
+
+  void ComputeSpillMask() OVERRIDE;
+
+  vixl::aarch32::Label* GetFrameEntryLabel() { return &frame_entry_label_; }
+
+  // Check if the desired_string_load_kind is supported. If it is, return it,
+  // otherwise return a fall-back kind that should be used instead.
+  HLoadString::LoadKind GetSupportedLoadStringKind(
+      HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
+  // Check if the desired_class_load_kind is supported. If it is, return it,
+  // otherwise return a fall-back kind that should be used instead.
+  HLoadClass::LoadKind GetSupportedLoadClassKind(
+      HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
+
+  // Check if the desired_dispatch_info is supported. If it is, return it,
+  // otherwise return a fall-back info that should be used instead.
+  HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
+      const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+      HInvokeStaticOrDirect* invoke) OVERRIDE;
+
+  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
+  void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
+
+  void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
+
+  // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
+  // and boot image strings/types. The only difference is the interpretation of the
+  // offset_or_index. The PC-relative address is loaded with three instructions,
+  // MOVW+MOVT to load the offset to base_reg and then ADD base_reg, PC. The offset
+  // is calculated from the ADD's effective PC, i.e. PC+4 on Thumb2. Though we
+  // currently emit these 3 instructions together, instruction scheduling could
+  // split this sequence apart, so we keep separate labels for each of them.
+  struct PcRelativePatchInfo {
+    PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx)
+        : target_dex_file(dex_file), offset_or_index(off_or_idx) { }
+    PcRelativePatchInfo(PcRelativePatchInfo&& other) = default;
+
+    const DexFile& target_dex_file;
+    // Either the dex cache array element offset or the string/type index.
+    uint32_t offset_or_index;
+    vixl::aarch32::Label movw_label;
+    vixl::aarch32::Label movt_label;
+    vixl::aarch32::Label add_pc_label;
+  };
+
+  PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index);
+  PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
+  PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+                                                       uint32_t element_offset);
+  void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
+
   // Fast path implementation of ReadBarrier::Barrier for a heap
   // reference field load when Baker's read barriers are used.
   void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -679,33 +634,35 @@
                                     uint32_t offset,
                                     Location index = Location::NoLocation());
 
-  // Check if the desired_string_load_kind is supported. If it is, return it,
-  // otherwise return a fall-back kind that should be used instead.
-  HLoadString::LoadKind GetSupportedLoadStringKind(
-      HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
-
-  // Check if the desired_class_load_kind is supported. If it is, return it,
-  // otherwise return a fall-back kind that should be used instead.
-  HLoadClass::LoadKind GetSupportedLoadClassKind(
-      HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
-
-  // Check if the desired_dispatch_info is supported. If it is, return it,
-  // otherwise return a fall-back info that should be used instead.
-  HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
-      const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke) OVERRIDE;
-
-  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
-  void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
-
-  void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
-
   void GenerateNop() OVERRIDE;
 
+  void GenerateImplicitNullCheck(HNullCheck* instruction) OVERRIDE;
+  void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE;
+
+  JumpTableARMVIXL* CreateJumpTable(HPackedSwitch* switch_instr) {
+    jump_tables_.emplace_back(new (GetGraph()->GetArena()) JumpTableARMVIXL(switch_instr));
+    return jump_tables_.back().get();
+  }
+  void EmitJumpTables();
+
+  void EmitMovwMovtPlaceholder(CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
+                               vixl::aarch32::Register out);
+
  private:
   vixl::aarch32::Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
                                                                 vixl::aarch32::Register temp);
 
+  using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::aarch32::Literal<uint32_t>*>;
+  using MethodToLiteralMap =
+      ArenaSafeMap<MethodReference, vixl::aarch32::Literal<uint32_t>*, MethodReferenceComparator>;
+
+  PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
+                                          uint32_t offset_or_index,
+                                          ArenaDeque<PcRelativePatchInfo>* patches);
+  template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
+  static void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos,
+                                          ArenaVector<LinkerPatch>* linker_patches);
+
   // Labels for each block that will be compiled.
   // We use a deque so that the `vixl::aarch32::Label` objects do not move in memory.
   ArenaDeque<vixl::aarch32::Label> block_labels_;  // Indexed by block id.
@@ -719,15 +676,19 @@
   ArmVIXLAssembler assembler_;
   const ArmInstructionSetFeatures& isa_features_;
 
+  // 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_;
+  // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
+  ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+  // PC-relative type patch info.
+  ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
+
   DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL);
 };
 
-#undef FOR_EACH_IMPLEMENTED_INSTRUCTION
-#undef FOR_EACH_UNIMPLEMENTED_INSTRUCTION
-#undef DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR
-#undef DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR
-
-
 }  // namespace arm
 }  // namespace art
 
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index d3623f1..eabdbad 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -17,6 +17,11 @@
 #ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
 #define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
 
+#include "debug/dwarf/register.h"
+#include "locations.h"
+#include "nodes.h"
+#include "utils/arm/constants_arm.h"
+
 // TODO(VIXL): Make VIXL compile with -Wshadow.
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wshadow"
diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc
index 82b8123..10a36c6 100644
--- a/compiler/optimizing/dex_cache_array_fixups_arm.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc
@@ -17,12 +17,24 @@
 #include "dex_cache_array_fixups_arm.h"
 
 #include "base/arena_containers.h"
+#ifdef ART_USE_VIXL_ARM_BACKEND
+#include "code_generator_arm_vixl.h"
+#include "intrinsics_arm_vixl.h"
+#else
 #include "code_generator_arm.h"
 #include "intrinsics_arm.h"
+#endif
 #include "utils/dex_cache_arrays_layout-inl.h"
 
 namespace art {
 namespace arm {
+#ifdef ART_USE_VIXL_ARM_BACKEND
+typedef CodeGeneratorARMVIXL CodeGeneratorARMType;
+typedef IntrinsicLocationsBuilderARMVIXL IntrinsicLocationsBuilderARMType;
+#else
+typedef CodeGeneratorARM CodeGeneratorARMType;
+typedef IntrinsicLocationsBuilderARM IntrinsicLocationsBuilderARMType;
+#endif
 
 /**
  * Finds instructions that need the dex cache arrays base as an input.
@@ -31,7 +43,7 @@
  public:
   DexCacheArrayFixupsVisitor(HGraph* graph, CodeGenerator* codegen)
       : HGraphVisitor(graph),
-        codegen_(down_cast<CodeGeneratorARM*>(codegen)),
+        codegen_(down_cast<CodeGeneratorARMType*>(codegen)),
         dex_cache_array_bases_(std::less<const DexFile*>(),
                                // Attribute memory use to code generator.
                                graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {}
@@ -66,7 +78,7 @@
     // If this is an invoke with PC-relative access to the dex cache methods array,
     // we need to add the dex cache arrays base as the special input.
     if (invoke->HasPcRelativeDexCache() &&
-        !IsCallFreeIntrinsic<IntrinsicLocationsBuilderARM>(invoke, codegen_)) {
+        !IsCallFreeIntrinsic<IntrinsicLocationsBuilderARMType>(invoke, codegen_)) {
       HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile());
       // Update the element offset in base.
       DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFile());
@@ -94,7 +106,7 @@
     return base;
   }
 
-  CodeGeneratorARM* codegen_;
+  CodeGeneratorARMType* codegen_;
 
   using DexCacheArraysBaseMap =
       ArenaSafeMap<const DexFile*, HArmDexCacheArraysBase*, std::less<const DexFile*>>;
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index f2602fb..1561502 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -218,20 +218,21 @@
   } else if (instruction->IsSub()) {
     info = TransferAddSub(LookupInfo(loop, instruction->InputAt(0)),
                           LookupInfo(loop, instruction->InputAt(1)), kSub);
+  } else if (instruction->IsNeg()) {
+    info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
   } else if (instruction->IsMul()) {
     info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
                        LookupInfo(loop, instruction->InputAt(1)));
   } else if (instruction->IsShl()) {
-    info = TransferShl(LookupInfo(loop, instruction->InputAt(0)),
-                       LookupInfo(loop, instruction->InputAt(1)),
-                       instruction->InputAt(0)->GetType());
-  } else if (instruction->IsNeg()) {
-    info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
+    HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+    if (mulc != nullptr) {
+      info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
+                         LookupInfo(loop, mulc));
+    }
   } else if (instruction->IsTypeConversion()) {
     info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)),
                        instruction->AsTypeConversion()->GetInputType(),
                        instruction->AsTypeConversion()->GetResultType());
-
   } else if (instruction->IsBoundsCheck()) {
     info = LookupInfo(loop, instruction->InputAt(0));  // Pass-through.
   }
@@ -271,7 +272,12 @@
   if (size == 1) {
     InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1);
     if (update != nullptr) {
-      AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update, type_));
+      AssignInfo(loop, phi, CreateInduction(kWrapAround,
+                                            kNop,
+                                            initial,
+                                            update,
+                                            /*fetch*/ nullptr,
+                                            type_));
     }
     return;
   }
@@ -289,6 +295,20 @@
     } else if (instruction->IsSub()) {
       update = SolveAddSub(
           loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true);
+    } else if (instruction->IsMul()) {
+      update = SolveGeo(
+          loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kMul);
+    } else if (instruction->IsDiv()) {
+      update = SolveGeo(
+          loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kDiv);
+    } else if (instruction->IsRem()) {
+      update = SolveGeo(
+          loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kNop);
+    } else if (instruction->IsShl()) {
+      HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+      if (mulc != nullptr) {
+        update = SolveGeo(loop, phi, instruction, instruction->InputAt(0), mulc, kMul);
+      }
     } else if (instruction->IsXor()) {
       update = SolveXor(loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1));
     } else if (instruction->IsEqual()) {
@@ -309,9 +329,14 @@
   if (induction != nullptr) {
     switch (induction->induction_class) {
       case kInvariant:
+        // Construct combined stride of the linear induction.
+        induction = CreateInduction(kLinear, kNop, induction, initial, /*fetch*/ nullptr, type_);
+        FALLTHROUGH_INTENDED;
+      case kPolynomial:
+      case kGeometric:
         // Classify first phi and then the rest of the cycle "on-demand".
         // Statements are scanned in order.
-        AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial, type_));
+        AssignInfo(loop, phi, induction);
         for (size_t i = 1; i < size; i++) {
           ClassifyTrivial(loop, scc_[i]);
         }
@@ -341,10 +366,19 @@
   //   (b, c, d, e, a)
   // in preparation of assigning this to the previous variable in the sequence.
   if (induction->induction_class == kInvariant) {
-    return CreateInduction(kPeriodic, induction, last, type_);
+    return CreateInduction(kPeriodic,
+                           kNop,
+                           induction,
+                           last,
+                           /*fetch*/ nullptr,
+                           type_);
   }
-  return CreateInduction(
-      kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last), type_);
+  return CreateInduction(kPeriodic,
+                         kNop,
+                         induction->op_a,
+                         RotatePeriodicInduction(induction->op_b, last),
+                         /*fetch*/ nullptr,
+                         type_);
 }
 
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop,
@@ -366,36 +400,55 @@
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(InductionInfo* a,
                                                                             InductionInfo* b,
                                                                             InductionOp op) {
-  // Transfer over an addition or subtraction: any invariant, linear, wrap-around, or periodic
-  // can be combined with an invariant to yield a similar result. Even two linear inputs can
-  // be combined. All other combinations fail, however.
+  // Transfer over an addition or subtraction: any invariant, linear, polynomial, geometric,
+  // wrap-around, or periodic can be combined with an invariant to yield a similar result.
+  // Even two linear inputs can be combined. 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) {
       return CreateInvariantOp(op, a, b);
     } else if (a->induction_class == kLinear && b->induction_class == kLinear) {
       return CreateInduction(kLinear,
+                             kNop,
                              TransferAddSub(a->op_a, b->op_a, op),
                              TransferAddSub(a->op_b, b->op_b, op),
+                             /*fetch*/ nullptr,
                              type_);
     } else if (a->induction_class == kInvariant) {
       InductionInfo* new_a = b->op_a;
       InductionInfo* new_b = TransferAddSub(a, b->op_b, op);
-      if (b->induction_class != kLinear) {
-        DCHECK(b->induction_class == kWrapAround || b->induction_class == kPeriodic);
+      if (b->induction_class == kWrapAround || b->induction_class == kPeriodic) {
         new_a = TransferAddSub(a, new_a, op);
       } else if (op == kSub) {  // Negation required.
         new_a = TransferNeg(new_a);
       }
-      return CreateInduction(b->induction_class, new_a, new_b, type_);
+      return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_);
     } else if (b->induction_class == kInvariant) {
       InductionInfo* new_a = a->op_a;
       InductionInfo* new_b = TransferAddSub(a->op_b, b, op);
-      if (a->induction_class != kLinear) {
-        DCHECK(a->induction_class == kWrapAround || a->induction_class == kPeriodic);
+      if (a->induction_class == kWrapAround || a->induction_class == kPeriodic) {
         new_a = TransferAddSub(new_a, b, op);
       }
-      return CreateInduction(a->induction_class, new_a, new_b, type_);
+      return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+    }
+  }
+  return nullptr;
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(InductionInfo* a) {
+  // 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) {
+      return CreateInvariantOp(kNeg, nullptr, a);
+    } else if (a->induction_class != kGeometric || a->operation == kMul) {
+      return CreateInduction(a->induction_class,
+                             a->operation,
+                             TransferNeg(a->op_a),
+                             TransferNeg(a->op_b),
+                             a->fetch,
+                             type_);
     }
   }
   return nullptr;
@@ -403,72 +456,45 @@
 
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(InductionInfo* a,
                                                                          InductionInfo* b) {
-  // Transfer over a multiplication: any invariant, linear, 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.
+  // Transfer over a multiplication: any invariant, linear, polynomial, geometric (mul),
+  // 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) {
       return CreateInvariantOp(kMul, a, b);
-    } else if (a->induction_class == kInvariant) {
+    } else if (a->induction_class == kInvariant && (b->induction_class != kGeometric ||
+                                                    b->operation == kMul)) {
       return CreateInduction(b->induction_class,
+                             b->operation,
                              TransferMul(a, b->op_a),
                              TransferMul(a, b->op_b),
+                             b->fetch,
                              type_);
-    } else if (b->induction_class == kInvariant) {
+    } else if (b->induction_class == kInvariant && (a->induction_class != kGeometric ||
+                                                    a->operation == kMul)) {
       return CreateInduction(a->induction_class,
+                             a->operation,
                              TransferMul(a->op_a, b),
                              TransferMul(a->op_b, b),
+                             a->fetch,
                              type_);
     }
   }
   return nullptr;
 }
 
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferShl(InductionInfo* a,
-                                                                         InductionInfo* b,
-                                                                         Primitive::Type type) {
-  // Transfer over a shift left: treat shift by restricted constant as equivalent multiplication.
-  int64_t value = -1;
-  if (a != nullptr && IsExact(b, &value)) {
-    // Obtain the constant needed for the multiplication. This yields an existing instruction
-    // if the constants is already there. Otherwise, this has a side effect on the HIR.
-    // The restriction on the shift factor avoids generating a negative constant
-    // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization
-    // for shift factors outside [0,32) and [0,64) ranges is done by earlier simplification.
-    if ((type == Primitive::kPrimInt  && 0 <= value && value < 31) ||
-        (type == Primitive::kPrimLong && 0 <= value && value < 63)) {
-      return TransferMul(a, CreateConstant(1 << value, type));
-    }
-  }
-  return nullptr;
-}
-
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(InductionInfo* a) {
-  // Transfer over a unary negation: an invariant, linear, 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) {
-      return CreateInvariantOp(kNeg, nullptr, a);
-    }
-    return CreateInduction(a->induction_class, TransferNeg(a->op_a), TransferNeg(a->op_b), type_);
-  }
-  return nullptr;
-}
-
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a,
                                                                          Primitive::Type from,
                                                                          Primitive::Type to) {
   if (a != nullptr) {
-    // Allow narrowing conversion in certain cases.
+    // 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, a->op_a, a->op_b, to);
+          return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to);
         }
       }
-      // TODO: other cases useful too?
     }
   }
   return nullptr;
@@ -511,11 +537,11 @@
     if (a != nullptr && a->induction_class == kInvariant) {
       if (phi->InputAt(1) == entry_phi) {
         InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
-        return CreateInduction(kPeriodic, a, initial, type_);
+        return CreateInduction(kPeriodic, kNop, a, initial, /*fetch*/ nullptr, type_);
       }
       InductionInfo* b = SolvePhi(phi, /* input_index */ 1);
       if (b != nullptr && b->induction_class == kPeriodic) {
-        return CreateInduction(kPeriodic, a, b, type_);
+        return CreateInduction(kPeriodic, kNop, a, b, /*fetch*/ nullptr, type_);
       }
     }
   }
@@ -530,7 +556,7 @@
                                                                          InductionOp op,
                                                                          bool is_first_call) {
   // Solve within a cycle over an addition or subtraction: adding or subtracting an
-  // invariant value, seeded from phi, keeps adding to the stride of the induction.
+  // invariant value, seeded from phi, keeps adding to the stride of the linear induction.
   InductionInfo* b = LookupInfo(loop, y);
   if (b != nullptr && b->induction_class == kInvariant) {
     if (x == entry_phi) {
@@ -553,12 +579,17 @@
     }
   } else if (op == kSub) {
     // Solve within a tight cycle that is formed by exactly two instructions,
-    // one phi and one update, for a periodic idiom of the form k = c - k;
+    // one phi and one update, for a periodic idiom of the form k = c - k.
     if (y == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
       InductionInfo* a = LookupInfo(loop, x);
       if (a != nullptr && a->induction_class == kInvariant) {
         InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
-        return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial, type_);
+        return CreateInduction(kPeriodic,
+                               kNop,
+                               CreateInvariantOp(kSub, a, initial),
+                               initial,
+                               /*fetch*/ nullptr,
+                               type_);
       }
     }
   }
@@ -566,21 +597,54 @@
   return nullptr;
 }
 
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveGeo(HLoopInformation* loop,
+                                                                      HInstruction* entry_phi,
+                                                                      HInstruction* instruction,
+                                                                      HInstruction* x,
+                                                                      HInstruction* y,
+                                                                      InductionOp op) {
+  // Solve within a tight cycle that is formed by exactly two instructions, one phi and
+  // one update, for a geometric induction of the form k = k * c.
+  if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
+    InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
+    InductionInfo* b = LookupInfo(loop, y);
+    if (b != nullptr && b->induction_class == kInvariant && b->operation == kFetch) {
+      return CreateInduction(kGeometric,
+                             op,
+                             initial,
+                             CreateConstant(0, type_),
+                             b->fetch,
+                             type_);
+    }
+  }
+  return nullptr;
+}
+
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveXor(HLoopInformation* loop,
                                                                       HInstruction* entry_phi,
                                                                       HInstruction* instruction,
                                                                       HInstruction* x,
                                                                       HInstruction* y) {
-  // Solve within a tight cycle on x = c ^ x or x = x ^ c.
+  // Solve within a tight cycle on periodic idiom of the form x = c ^ x or x = x ^ c.
   if (entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
     InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
     InductionInfo* a = LookupInfo(loop, x);
     if (a != nullptr && a->induction_class == kInvariant && entry_phi == y) {
-      return CreateInduction(kPeriodic, CreateInvariantOp(kXor, a, initial), initial, type_);
+      return CreateInduction(kPeriodic,
+                             kNop,
+                             CreateInvariantOp(kXor, a, initial),
+                             initial,
+                             /*fetch*/ nullptr,
+                             type_);
     }
     InductionInfo* b = LookupInfo(loop, y);
     if (b != nullptr && b->induction_class == kInvariant && entry_phi == x) {
-      return CreateInduction(kPeriodic, CreateInvariantOp(kXor, initial, b), initial, type_);
+      return CreateInduction(kPeriodic,
+                             kNop,
+                             CreateInvariantOp(kXor, initial, b),
+                             initial,
+                             /*fetch*/ nullptr,
+                             type_);
     }
   }
   return nullptr;
@@ -590,7 +654,7 @@
                                                                        HInstruction* entry_phi,
                                                                        HInstruction* instruction,
                                                                        int64_t opposite_value) {
-  // Detect hidden XOR construction in tight cycles on x = (x == 0) or x = (x != 1).
+  // Detect hidden XOR construction in x = (x == false) or x = (x != true).
   int64_t value = -1;
   HInstruction* x = instruction->InputAt(0);
   HInstruction* y = instruction->InputAt(1);
@@ -881,11 +945,14 @@
 
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateConstant(int64_t value,
                                                                             Primitive::Type type) {
-  if (type == Primitive::kPrimInt) {
-    return CreateInvariantFetch(graph_->GetIntConstant(value));
+  HInstruction* constant;
+  switch (type) {
+    case Primitive::kPrimDouble: constant = graph_->GetDoubleConstant(value); break;
+    case Primitive::kPrimFloat:  constant = graph_->GetFloatConstant(value);  break;
+    case Primitive::kPrimLong:   constant = graph_->GetLongConstant(value);   break;
+    default:                     constant = graph_->GetIntConstant(value);    break;
   }
-  DCHECK_EQ(type, Primitive::kPrimLong);
-  return CreateInvariantFetch(graph_->GetLongConstant(value));
+  return CreateInvariantFetch(constant);
 }
 
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInvariant(
@@ -948,6 +1015,26 @@
   return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type);
 }
 
+HInstruction* HInductionVarAnalysis::GetMultConstantForShift(HLoopInformation* loop,
+                                                             HInstruction* instruction) {
+  // Obtain the constant needed to treat shift as equivalent multiplication. This yields an
+  // existing instruction if the constants is already there. Otherwise, this has a side effect
+  // on the HIR. The restriction on the shift factor avoids generating a negative constant
+  // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization for
+  // shift factors outside [0,32) and [0,64) ranges is done by earlier simplification.
+  InductionInfo* b = LookupInfo(loop, instruction->InputAt(1));
+  int64_t value = -1;
+  if (IsExact(b, &value)) {
+    Primitive::Type type = instruction->InputAt(0)->GetType();
+    if (type == Primitive::kPrimInt && 0 <= value && value < 31) {
+      return graph_->GetIntConstant(1 << value);
+    }
+    if (type == Primitive::kPrimLong && 0 <= value && value < 63) {
+      return graph_->GetLongConstant(1L << value);
+    }
+  }
+  return nullptr;
+}
 
 void HInductionVarAnalysis::AssignCycle(HPhi* phi) {
   ArenaSet<HInstruction*>* set = &cycles_.Put(phi, ArenaSet<HInstruction*>(
@@ -993,6 +1080,16 @@
   return info1 == info2;
 }
 
+std::string HInductionVarAnalysis::FetchToString(HInstruction* fetch) {
+  DCHECK(fetch != nullptr);
+  if (fetch->IsIntConstant()) {
+    return std::to_string(fetch->AsIntConstant()->GetValue());
+  } else if (fetch->IsLongConstant()) {
+    return std::to_string(fetch->AsLongConstant()->GetValue());
+  }
+  return std::to_string(fetch->GetId()) + ":" + fetch->DebugName();
+}
+
 std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
   if (info != nullptr) {
     if (info->induction_class == kInvariant) {
@@ -1010,16 +1107,7 @@
         case kLE:    inv += " <= "; break;
         case kGT:    inv += " > ";  break;
         case kGE:    inv += " >= "; break;
-        case kFetch:
-          DCHECK(info->fetch);
-          if (info->fetch->IsIntConstant()) {
-            inv += std::to_string(info->fetch->AsIntConstant()->GetValue());
-          } else if (info->fetch->IsLongConstant()) {
-            inv += std::to_string(info->fetch->AsLongConstant()->GetValue());
-          } else {
-            inv += std::to_string(info->fetch->GetId()) + ":" + info->fetch->DebugName();
-          }
-          break;
+        case kFetch: inv += FetchToString(info->fetch); break;
         case kTripCountInLoop:       inv += " (TC-loop) ";        break;
         case kTripCountInBody:       inv += " (TC-body) ";        break;
         case kTripCountInLoopUnsafe: inv += " (TC-loop-unsafe) "; break;
@@ -1029,11 +1117,28 @@
       inv += ")";
       return inv;
     } else {
-      DCHECK(info->operation == kNop);
       if (info->induction_class == kLinear) {
         return "(" + InductionToString(info->op_a) + " * i + " +
                      InductionToString(info->op_b) + "):" +
                      Primitive::PrettyDescriptor(info->type);
+      } else if (info->induction_class == kPolynomial) {
+        return "poly( sum_i(" + InductionToString(info->op_a) + ") + " +
+                                InductionToString(info->op_b) + "):" +
+                                Primitive::PrettyDescriptor(info->type);
+      } else if (info->induction_class == kGeometric) {
+        DCHECK(info->fetch != nullptr);
+        if (info->operation == kNop) {
+         return "geo(" + InductionToString(info->op_a) + " mod_i " +
+                         FetchToString(info->fetch) + " + " +
+                         InductionToString(info->op_b) + "):" +
+                         Primitive::PrettyDescriptor(info->type);
+        } else {
+         return "geo(" + InductionToString(info->op_a) + " * " +
+                         FetchToString(info->fetch) +
+                         (info->operation == kMul ? " ^ i + " : " ^ -i + ") +
+                         InductionToString(info->op_b) + "):" +
+                         Primitive::PrettyDescriptor(info->type);
+        }
       } else if (info->induction_class == kWrapAround) {
         return "wrap(" + InductionToString(info->op_a) + ", " +
                          InductionToString(info->op_b) + "):" +
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 7027179..94afc71 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -51,14 +51,15 @@
   enum InductionClass {
     kInvariant,
     kLinear,
+    kPolynomial,
+    kGeometric,
     kWrapAround,
     kPeriodic
   };
 
   enum InductionOp {
-    // No-operation: a true induction.
+    // Operations.
     kNop,
-    // Various invariant operations.
     kAdd,
     kSub,
     kNeg,
@@ -81,16 +82,18 @@
   /**
    * Defines a detected induction as:
    *   (1) invariant:
-   *         operation: a + b, a - b, -b, a * b, a / b
-   *       or:
-   *         fetch: fetch from HIR
+   *         op: a + b, a - b, -b, a * b, a / b, a % b, a ^ b, fetch
    *   (2) linear:
    *         nop: a * i + b
-   *   (3) wrap-around
+   *   (3) polynomial:                        // TODO: coming soon
+   *         nop: sum_i(a) + b, for linear a
+   *   (4) geometric:
+   *         op: a * fetch^i + b, a * fetch^-i + b, a mod_i fetch + b
+   *   (5) wrap-around
    *         nop: a, then defined by b
-   *   (4) periodic
+   *   (6) periodic
    *         nop: a, then defined by b (repeated when exhausted)
-   *   (5) trip-count:
+   *   (7) trip-count:
    *         tc: defined by a, taken-test in b
    */
   struct InductionInfo : public ArenaObject<kArenaAllocInductionVarAnalysis> {
@@ -138,11 +141,13 @@
   }
 
   InductionInfo* CreateInduction(InductionClass ic,
+                                 InductionOp op,
                                  InductionInfo* a,
                                  InductionInfo* b,
+                                 HInstruction* f,
                                  Primitive::Type type) {
     DCHECK(a != nullptr && b != nullptr);
-    return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr, type);
+    return new (graph_->GetArena()) InductionInfo(ic, op, a, b, f, type);
   }
 
   // Methods for analysis.
@@ -156,9 +161,8 @@
   // Transfer operations.
   InductionInfo* TransferPhi(HLoopInformation* loop, HInstruction* phi, size_t input_index);
   InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op);
-  InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
-  InductionInfo* TransferShl(InductionInfo* a, InductionInfo* b, Primitive::Type type);
   InductionInfo* TransferNeg(InductionInfo* a);
+  InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
   InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to);
 
   // Solvers.
@@ -173,6 +177,12 @@
                              HInstruction* y,
                              InductionOp op,
                              bool is_first_call);  // possibly swaps x and y to try again
+  InductionInfo* SolveGeo(HLoopInformation* loop,
+                          HInstruction* entry_phi,
+                          HInstruction* instruction,
+                          HInstruction* x,
+                          HInstruction* y,
+                          InductionOp op);
   InductionInfo* SolveXor(HLoopInformation* loop,
                           HInstruction* entry_phi,
                           HInstruction* instruction,
@@ -214,6 +224,7 @@
   InductionInfo* LookupInfo(HLoopInformation* loop, HInstruction* instruction);
   InductionInfo* CreateConstant(int64_t value, Primitive::Type type);
   InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
+  HInstruction* GetMultConstantForShift(HLoopInformation* loop, HInstruction* instruction);
   void AssignCycle(HPhi* phi);
   ArenaSet<HInstruction*>* LookupCycle(HPhi* phi);
 
@@ -224,6 +235,7 @@
 
   // Helpers.
   static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
+  static std::string FetchToString(HInstruction* fetch);
   static std::string InductionToString(InductionInfo* info);
 
   // TODO: fine tune the following data structures, only keep relevant data.
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 3425b88..2199c8e 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -84,6 +84,7 @@
     entry_->AddInstruction(parameter_);
     constant0_ = graph_->GetIntConstant(0);
     constant1_ = graph_->GetIntConstant(1);
+    constant2_ = graph_->GetIntConstant(2);
     constant100_ = graph_->GetIntConstant(100);
     float_constant0_ = graph_->GetFloatConstant(0.0f);
     return_->AddInstruction(new (&allocator_) HReturnVoid());
@@ -191,6 +192,7 @@
   HInstruction* parameter_;  // "this"
   HInstruction* constant0_;
   HInstruction* constant1_;
+  HInstruction* constant2_;
   HInstruction* constant100_;
   HInstruction* float_constant0_;
 
@@ -252,11 +254,11 @@
 TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
   // Setup:
   // for (int i = 0; i < 100; i++) {
-  //   k = 100 + i;
-  //   k = 100 - i;
-  //   k = 100 * i;
-  //   k = i << 1;
-  //   k = - i;
+  //   t = 100 + i;
+  //   t = 100 - i;
+  //   t = 100 * i;
+  //   t = i << 1;
+  //   t = - i;
   // }
   BuildLoopNest(1);
   HInstruction* add = InsertInstruction(
@@ -288,18 +290,20 @@
   //   a[k] = 0;
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
 
   HInstruction* add = InsertInstruction(
-      new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
+      new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
   HInstruction* store1 = InsertArrayStore(add, 0);
   HInstruction* sub = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
   HInstruction* store2 = InsertArrayStore(sub, 0);
-  k->AddInput(sub);
+  k_header->AddInput(sub);
   PerformInductionVarAnalysis();
 
+  EXPECT_STREQ("(((100) - (1)) * i + (0)):PrimInt",
+               GetInductionInfo(k_header, 0).c_str());
   EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
                GetInductionInfo(store1->InputAt(1), 0).c_str());
   EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
@@ -335,6 +339,7 @@
   k_header->AddInput(k_body);
   PerformInductionVarAnalysis();
 
+  EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
   EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
 
   // Both increments get same induction.
@@ -367,6 +372,153 @@
   PerformInductionVarAnalysis();
 
   EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+
+  // Both increments get same induction.
+  EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
+  EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricMulInduction) {
+  // Setup:
+  // k = 1;
+  // for (int i = 0; i < 100; i++) {
+  //   k = k * 100;  // geometric (x 100)
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant1_);
+
+  HInstruction* mul = InsertInstruction(
+      new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant100_), 0);
+  k_header->AddInput(mul);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("geo((1) * 100 ^ i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("geo((100) * 100 ^ i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricShlInductionAndDerived) {
+  // Setup:
+  // k = 1;
+  // for (int i = 0; i < 100; i++) {
+  //   t = k + 1;
+  //   k = k << 1;  // geometric (x 2)
+  //   t = k + 100;
+  //   t = k - 1;
+  //   t = - t;
+  //   t = k * 2;
+  //   t = k << 2;
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant1_);
+
+  HInstruction* add1 = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+  HInstruction* shl1 = InsertInstruction(
+      new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant1_), 0);
+  HInstruction* add2 = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, shl1, constant100_), 0);
+  HInstruction* sub = InsertInstruction(
+      new (&allocator_) HSub(Primitive::kPrimInt, shl1, constant1_), 0);
+  HInstruction* neg = InsertInstruction(
+      new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
+  HInstruction* mul = InsertInstruction(
+      new (&allocator_) HMul(Primitive::kPrimInt, shl1, constant2_), 0);
+  HInstruction* shl2 = InsertInstruction(
+      new (&allocator_) HShl(Primitive::kPrimInt, shl1, constant2_), 0);
+  k_header->AddInput(shl1);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("geo((1) * 2 ^ i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("geo((1) * 2 ^ i + (1)):PrimInt", GetInductionInfo(add1, 0).c_str());
+  EXPECT_STREQ("geo((2) * 2 ^ i + (0)):PrimInt", GetInductionInfo(shl1, 0).c_str());
+  EXPECT_STREQ("geo((2) * 2 ^ i + (100)):PrimInt", GetInductionInfo(add2, 0).c_str());
+  EXPECT_STREQ("geo((2) * 2 ^ i + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
+  EXPECT_STREQ("geo(( - (2)) * 2 ^ i + ( - ((0) - (1)))):PrimInt",
+               GetInductionInfo(neg, 0).c_str());
+  EXPECT_STREQ("geo(((2) * (2)) * 2 ^ i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
+  EXPECT_STREQ("geo(((2) * (4)) * 2 ^ i + (0)):PrimInt", GetInductionInfo(shl2, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricDivInductionAndDerived) {
+  // Setup:
+  // k = 1;
+  // for (int i = 0; i < 100; i++) {
+  //   t = k + 100;
+  //   t = k - 1;
+  //   t = - t
+  //   t = k * 2;
+  //   t = k << 2;
+  //   k = k / 100;  // geometric (/ 100)
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant1_);
+
+  HInstruction* add = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
+  HInstruction* sub = InsertInstruction(
+      new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant1_), 0);
+  HInstruction* neg = InsertInstruction(
+      new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
+  HInstruction* mul = InsertInstruction(
+      new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant2_), 0);
+  HInstruction* shl = InsertInstruction(
+      new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0);
+  HInstruction* div = InsertInstruction(
+      new (&allocator_) HDiv(Primitive::kPrimInt, k_header, constant100_, kNoDexPc), 0);
+  k_header->AddInput(div);
+  PerformInductionVarAnalysis();
+
+  // Note, only the phi in the cycle and direct additive derived are classified.
+  EXPECT_STREQ("geo((1) * 100 ^ -i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("geo((1) * 100 ^ -i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
+  EXPECT_STREQ("geo((1) * 100 ^ -i + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(neg, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(mul, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(shl, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(div, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricRemInductionAndDerived) {
+  // Setup:
+  // k = 1;
+  // for (int i = 0; i < 100; i++) {
+  //   t = k + 100;
+  //   t = k - 1;
+  //   t = -t
+  //   t = k * 2;
+  //   t = k << 2;
+  //   k = k % 100;  // geometric (% 100)
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant1_);
+
+  HInstruction* add = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
+  HInstruction* sub = InsertInstruction(
+      new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant1_), 0);
+  HInstruction* neg = InsertInstruction(
+      new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
+  HInstruction* mul = InsertInstruction(
+      new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant2_), 0);
+  HInstruction* shl = InsertInstruction(
+      new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0);
+  HInstruction* rem = InsertInstruction(
+      new (&allocator_) HRem(Primitive::kPrimInt, k_header, constant100_, kNoDexPc), 0);
+  k_header->AddInput(rem);
+  PerformInductionVarAnalysis();
+
+  // Note, only the phi in the cycle and direct additive derived are classified.
+  EXPECT_STREQ("geo((1) mod_i 100 + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("geo((1) mod_i 100 + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
+  EXPECT_STREQ("geo((1) mod_i 100 + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(neg, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(mul, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(shl, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(rem, 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
@@ -377,17 +529,20 @@
   //   k = 100 - i;
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
 
-  HInstruction* store = InsertArrayStore(k, 0);
+  HInstruction* store = InsertArrayStore(k_header, 0);
   HInstruction* sub = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
-  k->AddInput(sub);
+  k_header->AddInput(sub);
   PerformInductionVarAnalysis();
 
   EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
+               GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
                GetInductionInfo(store->InputAt(1), 0).c_str());
+  EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
@@ -400,13 +555,13 @@
   //   t = 100 - i;
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
   HPhi* t = InsertLoopPhi(1, 0);
   t->AddInput(constant100_);
 
-  HInstruction* store = InsertArrayStore(k, 0);
-  k->AddInput(t);
+  HInstruction* store = InsertArrayStore(k_header, 0);
+  k_header->AddInput(t);
   HInstruction* sub = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
   t->AddInput(sub);
@@ -426,23 +581,27 @@
   //   t = k << 1;
   //   t = - k;
   //   k = i << 1;
+  //   t = - k;
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
 
   HInstruction* add = InsertInstruction(
-      new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
+      new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
   HInstruction* sub = InsertInstruction(
-      new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0);
+      new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant100_), 0);
   HInstruction* mul = InsertInstruction(
-      new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0);
-  HInstruction* shl = InsertInstruction(
-      new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0);
-  HInstruction* neg = InsertInstruction(
-      new (&allocator_) HNeg(Primitive::kPrimInt, k), 0);
-  k->AddInput(
-      InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
+      new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant100_), 0);
+  HInstruction* shl1 = InsertInstruction(
+      new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant1_), 0);
+  HInstruction* neg1 = InsertInstruction(
+      new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
+  HInstruction* shl2 = InsertInstruction(
+      new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
+  HInstruction* neg2 = InsertInstruction(
+      new (&allocator_) HNeg(Primitive::kPrimInt, shl2), 0);
+  k_header->AddInput(shl2);
   PerformInductionVarAnalysis();
 
   EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt",
@@ -452,9 +611,11 @@
   EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt",
                GetInductionInfo(mul, 0).c_str());
   EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt",
-               GetInductionInfo(shl, 0).c_str());
+               GetInductionInfo(shl1, 0).c_str());
   EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
-               GetInductionInfo(neg, 0).c_str());
+               GetInductionInfo(neg1, 0).c_str());
+  EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl2, 0).c_str());
+  EXPECT_STREQ("(( - (2)) * i + (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
@@ -470,15 +631,15 @@
   //   k = d;
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
   HPhi* t = InsertLoopPhi(1, 0);
   t->AddInput(constant100_);
 
-  HInstruction* store1 = InsertArrayStore(k, 0);
+  HInstruction* store1 = InsertArrayStore(k_header, 0);
   HInstruction* store2 = InsertArrayStore(t, 0);
-  k->AddInput(t);
-  t->AddInput(k);
+  k_header->AddInput(t);
+  t->AddInput(k_header);
   PerformInductionVarAnalysis();
 
   EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str());
@@ -493,13 +654,13 @@
   //   k = 1 - k;
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
 
-  HInstruction* store = InsertArrayStore(k, 0);
+  HInstruction* store = InsertArrayStore(k_header, 0);
   HInstruction* sub = InsertInstruction(
-      new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0);
-  k->AddInput(sub);
+      new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
+  k_header->AddInput(sub);
   PerformInductionVarAnalysis();
 
   EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
@@ -514,13 +675,13 @@
   //   k = k ^ 1;
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
 
-  HInstruction* store = InsertArrayStore(k, 0);
+  HInstruction* store = InsertArrayStore(k_header, 0);
   HInstruction* x = InsertInstruction(
-      new (&allocator_) HXor(Primitive::kPrimInt, k, constant1_), 0);
-  k->AddInput(x);
+      new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant1_), 0);
+  k_header->AddInput(x);
   PerformInductionVarAnalysis();
 
   EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
@@ -534,14 +695,15 @@
   //   k = 1 ^ k;
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant1_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant1_);
 
   HInstruction* x = InsertInstruction(
-      new (&allocator_) HXor(Primitive::kPrimInt, constant1_, k), 0);
-  k->AddInput(x);
+      new (&allocator_) HXor(Primitive::kPrimInt, constant1_, k_header), 0);
+  k_header->AddInput(x);
   PerformInductionVarAnalysis();
 
+  EXPECT_STREQ("periodic((1), ((1) ^ (1))):PrimInt", GetInductionInfo(k_header, 0).c_str());
   EXPECT_STREQ("periodic(((1) ^ (1)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
 }
 
@@ -552,14 +714,15 @@
   //   k = k ^ 100;
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant1_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant1_);
 
   HInstruction* x = InsertInstruction(
-      new (&allocator_) HXor(Primitive::kPrimInt, k, constant100_), 0);
-  k->AddInput(x);
+      new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant100_), 0);
+  k_header->AddInput(x);
   PerformInductionVarAnalysis();
 
+  EXPECT_STREQ("periodic((1), ((1) ^ (100))):PrimInt", GetInductionInfo(k_header, 0).c_str());
   EXPECT_STREQ("periodic(((1) ^ (100)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
 }
 
@@ -570,13 +733,14 @@
   //   k = (k == 0);
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
 
-  HInstruction* x = InsertInstruction(new (&allocator_) HEqual(k, constant0_), 0);
-  k->AddInput(x);
+  HInstruction* x = InsertInstruction(new (&allocator_) HEqual(k_header, constant0_), 0);
+  k_header->AddInput(x);
   PerformInductionVarAnalysis();
 
+  EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
   EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
 }
 
@@ -587,13 +751,14 @@
   //   k = (0 == k);
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
 
-  HInstruction* x = InsertInstruction(new (&allocator_) HEqual(constant0_, k), 0);
-  k->AddInput(x);
+  HInstruction* x = InsertInstruction(new (&allocator_) HEqual(constant0_, k_header), 0);
+  k_header->AddInput(x);
   PerformInductionVarAnalysis();
 
+  EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
   EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
 }
 
@@ -604,13 +769,14 @@
   //   k = (k != 1);
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
 
-  HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(k, constant1_), 0);
-  k->AddInput(x);
+  HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(k_header, constant1_), 0);
+  k_header->AddInput(x);
   PerformInductionVarAnalysis();
 
+  EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
   EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
 }
 
@@ -621,13 +787,14 @@
   //   k = (1 != k);
   // }
   BuildLoopNest(1);
-  HPhi* k = InsertLoopPhi(0, 0);
-  k->AddInput(constant0_);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
 
-  HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(constant1_, k), 0);
-  k->AddInput(x);
+  HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(constant1_, k_header), 0);
+  k_header->AddInput(x);
   PerformInductionVarAnalysis();
 
+  EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
   EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
 }
 
@@ -635,6 +802,7 @@
   // Setup:
   // k = 0;
   // for (int i = 0; i < 100; i++) {
+  //   t = - k;
   //   k = 1 - k;
   //   t = k + 100;
   //   t = k - 100;
@@ -646,28 +814,31 @@
   HPhi* k_header = InsertLoopPhi(0, 0);
   k_header->AddInput(constant0_);
 
-  HInstruction* k_body = InsertInstruction(
+  HInstruction* neg1 = InsertInstruction(
+      new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
+  HInstruction* idiom = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
-  k_header->AddInput(k_body);
-
-  // Derived expressions.
   HInstruction* add = InsertInstruction(
-      new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0);
+      new (&allocator_) HAdd(Primitive::kPrimInt, idiom, constant100_), 0);
   HInstruction* sub = InsertInstruction(
-      new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0);
+      new (&allocator_) HSub(Primitive::kPrimInt, idiom, constant100_), 0);
   HInstruction* mul = InsertInstruction(
-      new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0);
+      new (&allocator_) HMul(Primitive::kPrimInt, idiom, constant100_), 0);
   HInstruction* shl = InsertInstruction(
-      new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0);
-  HInstruction* neg = InsertInstruction(
-      new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
+      new (&allocator_) HShl(Primitive::kPrimInt, idiom, constant1_), 0);
+  HInstruction* neg2 = InsertInstruction(
+      new (&allocator_) HNeg(Primitive::kPrimInt, idiom), 0);
+  k_header->AddInput(idiom);
   PerformInductionVarAnalysis();
 
+  EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("periodic((0), ( - (1))):PrimInt", GetInductionInfo(neg1, 0).c_str());
+  EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(idiom, 0).c_str());
   EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str());
   EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str());
   EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
   EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
-  EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
+  EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
@@ -683,18 +854,18 @@
   // }
   BuildLoopNest(10);
 
-  HPhi* k[10];
+  HPhi* k_header[10];
   for (int d = 0; d < 10; d++) {
-    k[d] = InsertLoopPhi(0, d);
+    k_header[d] = InsertLoopPhi(0, d);
   }
 
   HInstruction* inc = InsertInstruction(
-      new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9);
+      new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k_header[9]), 9);
   HInstruction* store = InsertArrayStore(inc, 9);
 
   for (int d = 0; d < 10; d++) {
-    k[d]->AddInput((d != 0) ? k[d - 1] : constant0_);
-    k[d]->AddInput((d != 9) ? k[d + 1] : inc);
+    k_header[d]->AddInput((d != 0) ? k_header[d - 1] : constant0_);
+    k_header[d]->AddInput((d != 9) ? k_header[d + 1] : inc);
   }
   PerformInductionVarAnalysis();
 
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 235793d..75619a3 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -57,6 +57,21 @@
   return false;
 }
 
+/** Returns b^e for b,e >= 1. */
+static int64_t IntPow(int64_t b, int64_t e) {
+  DCHECK_GE(b, 1);
+  DCHECK_GE(e, 1);
+  int64_t pow = 1;
+  while (e) {
+    if (e & 1) {
+      pow *= b;
+    }
+    e >>= 1;
+    b *= b;
+  }
+  return pow;
+}
+
 /**
  * Detects an instruction that is >= 0. As long as the value is carried by
  * a single instruction, arithmetic wrap-around cannot occur.
@@ -399,6 +414,8 @@
     /*out*/ HLoopInformation** loop,
     /*out*/ HInductionVarAnalysis::InductionInfo** info,
     /*out*/ HInductionVarAnalysis::InductionInfo** trip) const {
+  DCHECK(context != nullptr);
+  DCHECK(context->GetBlock() != nullptr);
   HLoopInformation* lp = context->GetBlock()->GetLoopInformation();  // closest enveloping loop
   if (lp != nullptr) {
     HInductionVarAnalysis::InductionInfo* i = induction_analysis_->LookupInfo(lp, instruction);
@@ -474,6 +491,8 @@
                                                       HInductionVarAnalysis::InductionInfo* trip,
                                                       bool in_body,
                                                       bool is_min) const {
+  DCHECK(info != nullptr);
+  DCHECK(info->induction_class == HInductionVarAnalysis::kLinear);
   // Detect common situation where an offset inside the trip-count cancels out during range
   // analysis (finding max a * (TC - 1) + OFFSET for a == 1 and TC = UPPER - OFFSET or finding
   // min a * (TC - 1) + OFFSET for a == -1 and TC = OFFSET - UPPER) to avoid losing information
@@ -520,6 +539,30 @@
                   GetVal(info->op_b, trip, in_body, is_min));
 }
 
+InductionVarRange::Value InductionVarRange::GetGeometric(HInductionVarAnalysis::InductionInfo* info,
+                                                         HInductionVarAnalysis::InductionInfo* trip,
+                                                         bool in_body,
+                                                         bool is_min) const {
+  DCHECK(info != nullptr);
+  DCHECK(info->induction_class == HInductionVarAnalysis::kGeometric);
+  int64_t a = 0;
+  int64_t f = 0;
+  if (IsConstant(info->op_a, kExact, &a) &&
+      CanLongValueFitIntoInt(a) &&
+      IsIntAndGet(info->fetch, &f) &&
+      CanLongValueFitIntoInt(f) && f >= 1) {
+    // Conservative bounds on a * f^-i + b with f >= 1 can be computed without trip count.
+    // Same for mod. All other forms would require a much more elaborate evaluation.
+    const bool is_min_a = a >= 0 ? is_min : !is_min;
+    if (info->operation == HInductionVarAnalysis::kDiv) {
+      return AddValue(Value(is_min_a ? 0 : a), GetVal(info->op_b, trip, in_body, is_min));
+    } else if (info->operation == HInductionVarAnalysis::kNop) {
+      return AddValue(Value(is_min_a ? (a % f) : a), GetVal(info->op_b, trip, in_body, is_min));
+    }
+  }
+  return Value();
+}
+
 InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
                                                      HInductionVarAnalysis::InductionInfo* trip,
                                                      bool in_body,
@@ -631,6 +674,10 @@
         break;
       case HInductionVarAnalysis::kLinear:
         return CorrectForType(GetLinear(info, trip, in_body, is_min), info->type);
+      case HInductionVarAnalysis::kPolynomial:
+        break;
+      case HInductionVarAnalysis::kGeometric:
+        return GetGeometric(info, trip, in_body, is_min);
       case HInductionVarAnalysis::kWrapAround:
       case HInductionVarAnalysis::kPeriodic:
         return MergeVal(GetVal(info->op_a, trip, in_body, is_min),
@@ -842,17 +889,21 @@
   *needs_taken_test = IsBodyTripCount(trip);
   // Handle last value request.
   if (is_last_value) {
-    if (info->induction_class == HInductionVarAnalysis::kLinear) {
-      if (*stride_value > 0) {
-        lower = nullptr;
-      } else {
-        upper = nullptr;
-      }
-    } else if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
-      DCHECK(!in_body);
-      return GenerateLastValuePeriodic(info, trip, graph, block, lower, needs_taken_test);
-    } else {
-      return false;
+    DCHECK(!in_body);
+    switch (info->induction_class) {
+      case HInductionVarAnalysis::kLinear:
+        if (*stride_value > 0) {
+          lower = nullptr;
+        } else {
+          upper = nullptr;
+        }
+        break;
+      case HInductionVarAnalysis::kGeometric:
+        return GenerateLastValueGeometric(info, trip, graph, block, lower);
+      case HInductionVarAnalysis::kPeriodic:
+        return GenerateLastValuePeriodic(info, trip, graph, block, lower, needs_taken_test);
+      default:
+        return false;
     }
   }
   // Code generation for taken test: generate the code when requested or otherwise analyze
@@ -874,12 +925,68 @@
       GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false);
 }
 
+bool InductionVarRange::GenerateLastValueGeometric(HInductionVarAnalysis::InductionInfo* info,
+                                                   HInductionVarAnalysis::InductionInfo* trip,
+                                                   HGraph* graph,
+                                                   HBasicBlock* block,
+                                                   /*out*/HInstruction** result) const {
+  DCHECK(info != nullptr);
+  DCHECK(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) {
+    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)) {
+      // Generate a % f + b.
+      if (info->operation == HInductionVarAnalysis::kNop) {
+        if (graph != nullptr) {
+          HInstruction* rem =
+              Insert(block, new (graph->GetArena()) HRem(info->type, opa, info->fetch, kNoDexPc));
+          *result = Insert(block, new (graph->GetArena()) HAdd(info->type, rem, opb));
+        }
+        return true;
+      }
+      // Compute f ^ t.
+      int64_t fpowt = IntPow(f, t);
+      if (graph != nullptr) {
+        DCHECK(info->type == Primitive::kPrimInt);  // due to codegen, generalize?
+        if (fpowt == 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));
+        } 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));
+        }
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
 bool InductionVarRange::GenerateLastValuePeriodic(HInductionVarAnalysis::InductionInfo* info,
                                                   HInductionVarAnalysis::InductionInfo* trip,
                                                   HGraph* graph,
                                                   HBasicBlock* block,
                                                   /*out*/HInstruction** result,
                                                   /*out*/bool* needs_taken_test) const {
+  DCHECK(info != nullptr);
   DCHECK(info->induction_class == HInductionVarAnalysis::kPeriodic);
   // Count period.
   int32_t period = 1;
@@ -937,6 +1044,7 @@
       return true;
     }
     // Verify type safety.
+    // TODO: generalize
     Primitive::Type type = Primitive::kPrimInt;
     if (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean) {
       return false;
@@ -1058,6 +1166,9 @@
         }
         break;
       }
+      case HInductionVarAnalysis::kPolynomial:
+      case HInductionVarAnalysis::kGeometric:
+        break;
       case HInductionVarAnalysis::kWrapAround:
       case HInductionVarAnalysis::kPeriodic: {
         // Wrap-around and periodic inductions are restricted to constants only, so that extreme
@@ -1071,8 +1182,6 @@
         }
         break;
       }
-      default:
-        break;
     }
   }
   return false;
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 034cf32..f7360e8 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -190,6 +190,10 @@
                   HInductionVarAnalysis::InductionInfo* trip,
                   bool in_body,
                   bool is_min) const;
+  Value GetGeometric(HInductionVarAnalysis::InductionInfo* info,
+                     HInductionVarAnalysis::InductionInfo* trip,
+                     bool in_body,
+                     bool is_min) const;
   Value GetFetch(HInstruction* instruction,
                  HInductionVarAnalysis::InductionInfo* trip,
                  bool in_body,
@@ -245,6 +249,12 @@
                                 /*out*/ bool* needs_finite_test,
                                 /*out*/ bool* needs_taken_test) const;
 
+  bool GenerateLastValueGeometric(HInductionVarAnalysis::InductionInfo* info,
+                                  HInductionVarAnalysis::InductionInfo* trip,
+                                  HGraph* graph,
+                                  HBasicBlock* block,
+                                  /*out*/HInstruction** result) const;
+
   bool GenerateLastValuePeriodic(HInductionVarAnalysis::InductionInfo* info,
                                  HInductionVarAnalysis::InductionInfo* trip,
                                  HGraph* graph,
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index 4c99e3c..510841e 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -170,22 +170,46 @@
 
   /** Constructs a linear a * i + b induction. */
   HInductionVarAnalysis::InductionInfo* CreateLinear(int32_t a, int32_t b) {
-    return iva_->CreateInduction(
-        HInductionVarAnalysis::kLinear, CreateConst(a), CreateConst(b), Primitive::kPrimInt);
+    return iva_->CreateInduction(HInductionVarAnalysis::kLinear,
+                                 HInductionVarAnalysis::kNop,
+                                 CreateConst(a),
+                                 CreateConst(b),
+                                 nullptr,
+                                 Primitive::kPrimInt);
+  }
+
+  /** Constructs a geometric a * f^i + b induction. */
+  HInductionVarAnalysis::InductionInfo* CreateGeometric(int32_t a, int32_t b, int32_t f, char op) {
+    return iva_->CreateInduction(HInductionVarAnalysis::kGeometric,
+                                 op == '*' ? HInductionVarAnalysis::kMul :
+                                 op == '/' ? HInductionVarAnalysis::kDiv :
+                                             HInductionVarAnalysis::kNop,
+                                 CreateConst(a),
+                                 CreateConst(b),
+                                 graph_->GetIntConstant(f),
+                                 Primitive::kPrimInt);
   }
 
   /** Constructs a range [lo, hi] using a periodic induction. */
   HInductionVarAnalysis::InductionInfo* CreateRange(int32_t lo, int32_t hi) {
-    return iva_->CreateInduction(
-        HInductionVarAnalysis::kPeriodic, CreateConst(lo), CreateConst(hi), Primitive::kPrimInt);
+    return iva_->CreateInduction(HInductionVarAnalysis::kPeriodic,
+                                 HInductionVarAnalysis::kNop,
+                                 CreateConst(lo),
+                                 CreateConst(hi),
+                                 nullptr,
+                                 Primitive::kPrimInt);
   }
 
   /** Constructs a wrap-around induction consisting of a constant, followed info */
   HInductionVarAnalysis::InductionInfo* CreateWrapAround(
       int32_t initial,
       HInductionVarAnalysis::InductionInfo* info) {
-    return iva_->CreateInduction(
-        HInductionVarAnalysis::kWrapAround, CreateConst(initial), info, Primitive::kPrimInt);
+    return iva_->CreateInduction(HInductionVarAnalysis::kWrapAround,
+                                 HInductionVarAnalysis::kNop,
+                                 CreateConst(initial),
+                                 info,
+                                 nullptr,
+                                 Primitive::kPrimInt);
   }
 
   /** Constructs a wrap-around induction consisting of a constant, followed by a range. */
@@ -414,6 +438,33 @@
   ExpectEqual(Value(20), GetMax(CreateWrapAround(20, -1, 10), nullptr));
 }
 
+TEST_F(InductionVarRangeTest, GetMinMaxGeometricMul) {
+  ExpectEqual(Value(), GetMin(CreateGeometric(1, 1, 1, '*'), nullptr));
+  ExpectEqual(Value(), GetMax(CreateGeometric(1, 1, 1, '*'), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxGeometricDiv) {
+  ExpectEqual(Value(5), GetMin(CreateGeometric(11, 5, 3, '/'), nullptr));
+  ExpectEqual(Value(16), GetMax(CreateGeometric(11, 5, 3, '/'), nullptr));
+  ExpectEqual(Value(-5), GetMin(CreateGeometric(11, -5, 3, '/'), nullptr));
+  ExpectEqual(Value(6), GetMax(CreateGeometric(11, -5, 3, '/'), nullptr));
+  ExpectEqual(Value(-6), GetMin(CreateGeometric(-11, 5, 3, '/'), nullptr));
+  ExpectEqual(Value(5), GetMax(CreateGeometric(-11, 5, 3, '/'), nullptr));
+  ExpectEqual(Value(-16), GetMin(CreateGeometric(-11, -5, 3, '/'), nullptr));
+  ExpectEqual(Value(-5), GetMax(CreateGeometric(-11, -5, 3, '/'), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxGeometricRem) {
+  ExpectEqual(Value(7), GetMin(CreateGeometric(11, 5, 3, '%'), nullptr));
+  ExpectEqual(Value(16), GetMax(CreateGeometric(11, 5, 3, '%'), nullptr));
+  ExpectEqual(Value(-3), GetMin(CreateGeometric(11, -5, 3, '%'), nullptr));
+  ExpectEqual(Value(6), GetMax(CreateGeometric(11, -5, 3, '%'), nullptr));
+  ExpectEqual(Value(-6), GetMin(CreateGeometric(-11, 5, 3, '%'), nullptr));
+  ExpectEqual(Value(3), GetMax(CreateGeometric(-11, 5, 3, '%'), nullptr));
+  ExpectEqual(Value(-16), GetMin(CreateGeometric(-11, -5, 3, '%'), nullptr));
+  ExpectEqual(Value(-7), GetMax(CreateGeometric(-11, -5, 3, '%'), nullptr));
+}
+
 TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) {
   ExpectEqual(Value(-2), GetMin(CreateRange(-2, 99), nullptr));
   ExpectEqual(Value(99), GetMax(CreateRange(-2, 99), nullptr));
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 9e72447..433dced 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -71,7 +71,7 @@
       : SlowPathCodeARMVIXL(invoke), invoke_(invoke) {}
 
   Location MoveArguments(CodeGenerator* codegen) {
-    InvokeDexCallingConventionVisitorARM calling_convention_visitor;
+    InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
     IntrinsicVisitor::MoveArguments(invoke_, codegen, &calling_convention_visitor);
     return calling_convention_visitor.GetMethodLocation();
   }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index e15e33d..8ea2b06 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -630,10 +630,8 @@
 #if defined(ART_ENABLE_CODEGEN_arm)
     case kThumb2:
     case kArm: {
-#ifndef ART_USE_VIXL_ARM_BACKEND
       arm::DexCacheArrayFixups* fixups =
           new (arena) arm::DexCacheArrayFixups(graph, codegen, stats);
-#endif
       arm::InstructionSimplifierArm* simplifier =
           new (arena) arm::InstructionSimplifierArm(graph, stats);
       SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
@@ -642,9 +640,7 @@
         simplifier,
         side_effects,
         gvn,
-#ifndef ART_USE_VIXL_ARM_BACKEND
         fixups
-#endif
       };
       RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer);
       break;
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index fb6f172..2d026b8 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -428,8 +428,6 @@
   UNIMPLEMENTED(FATAL);
 }
 
-static constexpr uint32_t kArmInstrMaxSizeInBytes = 4;
-
 void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
                                                       FrameOffset handle_scope_offset,
                                                       ManagedRegister min_reg,
@@ -458,14 +456,14 @@
     if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
       if (!out_reg.Equals(in_reg)) {
         AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
-                                     3 * kArmInstrMaxSizeInBytes,
+                                     3 * vixl32::kMaxInstructionSizeInBytes,
                                      CodeBufferCheckScope::kMaximumSize);
         ___ it(eq, 0xc);
         ___ mov(eq, out_reg.AsVIXLRegister(), 0);
         asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
       } else {
         AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
-                                     2 * kArmInstrMaxSizeInBytes,
+                                     2 * vixl32::kMaxInstructionSizeInBytes,
                                      CodeBufferCheckScope::kMaximumSize);
         ___ it(ne, 0x8);
         asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
@@ -496,7 +494,7 @@
 
     if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
       AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
-                                   2 * kArmInstrMaxSizeInBytes,
+                                   2 * vixl32::kMaxInstructionSizeInBytes,
                                    CodeBufferCheckScope::kMaximumSize);
       ___ it(ne, 0x8);
       asm_.AddConstantInIt(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
@@ -589,7 +587,7 @@
   ___ Cmp(scratch.AsVIXLRegister(), 0);
   {
     AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
-                                 kArmInstrMaxSizeInBytes,
+                                 vixl32::kMaxInstructionSizeInBytes,
                                  CodeBufferCheckScope::kMaximumSize);
     ___ b(ne, Narrow, exception_blocks_.back()->Entry());
   }
diff --git a/compiler/utils/atomic_method_ref_map-inl.h b/compiler/utils/atomic_method_ref_map-inl.h
index 70ea028..d71c2fe 100644
--- a/compiler/utils/atomic_method_ref_map-inl.h
+++ b/compiler/utils/atomic_method_ref_map-inl.h
@@ -78,6 +78,15 @@
   }
 }
 
+template <typename T>
+inline void AtomicMethodRefMap<T>::ClearEntries() {
+  for (auto& it : arrays_) {
+    for (auto& element : it.second) {
+      element.StoreRelaxed(nullptr);
+    }
+  }
+}
+
 }  // namespace art
 
 #endif  // ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_INL_H_
diff --git a/compiler/utils/atomic_method_ref_map.h b/compiler/utils/atomic_method_ref_map.h
index 11ab211..fed848f 100644
--- a/compiler/utils/atomic_method_ref_map.h
+++ b/compiler/utils/atomic_method_ref_map.h
@@ -55,6 +55,8 @@
   template <typename Visitor>
   void Visit(const Visitor& visitor);
 
+  void ClearEntries();
+
  private:
   // Verified methods. The method array is fixed to avoid needing a lock to extend it.
   using ElementArray = dchecked_vector<Atomic<T>>;
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 90fe6da..52765f9 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -19,8 +19,11 @@
 
 #include "class_linker.h"
 #include "compiler/common_compiler_test.h"
+#include "compiler/dex/verification_results.h"
+#include "compiler/dex/verified_method.h"
 #include "compiler/driver/compiler_options.h"
 #include "compiler/driver/compiler_driver.h"
+#include "compiler/utils/atomic_method_ref_map-inl.h"
 #include "compiler_callbacks.h"
 #include "dex_file.h"
 #include "dex_file_types.h"
@@ -90,6 +93,14 @@
       verifier_deps_.reset(callbacks_->GetVerifierDeps());
     }
     callbacks_->SetVerifierDeps(nullptr);
+    // Clear entries in the verification results to avoid hitting a DCHECK that
+    // we always succeed inserting a new entry after verifying.
+    AtomicMethodRefMap<const VerifiedMethod*>* map =
+        &compiler_driver_->GetVerificationResults()->atomic_verified_methods_;
+    map->Visit([](const MethodReference& ref ATTRIBUTE_UNUSED, const VerifiedMethod* method) {
+      delete method;
+    });
+    map->ClearEntries();
   }
 
   void SetVerifierDeps(const std::vector<const DexFile*>& dex_files) {
@@ -112,6 +123,9 @@
     for (const DexFile* dex_file : dex_files_) {
       class_linker_->RegisterDexFile(*dex_file, loader.Get());
     }
+    for (const DexFile* dex_file : dex_files_) {
+      compiler_driver_->GetVerificationResults()->AddDexFile(dex_file);
+    }
   }
 
   void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 77d799f..96b6f18 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -79,7 +79,8 @@
   DCHECK(ext->GetObsoleteDexCaches() != nullptr);
   int32_t len = obsolete_methods->GetLength();
   DCHECK_EQ(len, ext->GetObsoleteDexCaches()->GetLength());
-  // TODO I think this is fine since images should never have obsolete methods in them.
+  // Using kRuntimePointerSize (instead of using the image's pointer size) is fine since images
+  // should never have obsolete methods in them so they should always be the same.
   PointerSize pointer_size = kRuntimePointerSize;
   DCHECK_EQ(kRuntimePointerSize, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
   for (int32_t i = 0; i < len; i++) {
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index 0d842cc..9bcda35 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -80,6 +80,11 @@
   return timed_out;
 }
 
+int Barrier::GetCount(Thread* self) {
+  MutexLock mu(self, lock_);
+  return count_;
+}
+
 void Barrier::SetCountLocked(Thread* self, int count) {
   count_ = count;
   if (count == 0) {
diff --git a/runtime/barrier.h b/runtime/barrier.h
index 94977fb..d7c4661 100644
--- a/runtime/barrier.h
+++ b/runtime/barrier.h
@@ -61,6 +61,8 @@
   // another thread is still in Wait().  See above.
   void Init(Thread* self, int count) REQUIRES(!lock_);
 
+  int GetCount(Thread* self) REQUIRES(!lock_);
+
  private:
   void SetCountLocked(Thread* self, int count) REQUIRES(lock_);
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 992d4bf..674bad7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -242,10 +242,11 @@
   ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
   CHECK(cause.get() != nullptr);
 
-  // Boot classpath classes should not fail initialization.
-  if (!Runtime::Current()->IsAotCompiler()) {
+  // Boot classpath classes should not fail initialization. This is a sanity debug check. This
+  // cannot in general be guaranteed, but in all likelihood leads to breakage down the line.
+  if (klass->GetClassLoader() == nullptr && !Runtime::Current()->IsAotCompiler()) {
     std::string tmp;
-    CHECK(klass->GetClassLoader() != nullptr) << klass->GetDescriptor(&tmp);
+    LOG(kIsDebugBuild ? FATAL : WARNING) << klass->GetDescriptor(&tmp) << " failed initialization";
   }
 
   env->ExceptionClear();
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 19ee0fb..fbab73f 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -812,13 +812,13 @@
   false_gray_stack_.clear();
 }
 
-
 void ConcurrentCopying::IssueEmptyCheckpoint() {
   Thread* self = Thread::Current();
   ThreadList* thread_list = Runtime::Current()->GetThreadList();
   Barrier* barrier = thread_list->EmptyCheckpointBarrier();
   barrier->Init(self, 0);
-  size_t barrier_count = thread_list->RunEmptyCheckpoint();
+  std::vector<uint32_t> runnable_thread_ids;  // Used in debug build only
+  size_t barrier_count = thread_list->RunEmptyCheckpoint(runnable_thread_ids);
   // If there are no threads to wait which implys that all the checkpoint functions are finished,
   // then no need to release the mutator lock.
   if (barrier_count == 0) {
@@ -828,7 +828,27 @@
   Locks::mutator_lock_->SharedUnlock(self);
   {
     ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
-    barrier->Increment(self, barrier_count);
+    if (kIsDebugBuild) {
+      static constexpr uint64_t kEmptyCheckpointTimeoutMs = 600 * 1000;  // 10 minutes.
+      bool timed_out = barrier->Increment(self, barrier_count, kEmptyCheckpointTimeoutMs);
+      if (timed_out) {
+        Runtime* runtime = Runtime::Current();
+        std::ostringstream ss;
+        ss << "Empty checkpoint timeout\n";
+        ss << "Barrier count " << barrier->GetCount(self) << "\n";
+        ss << "Runnable thread IDs";
+        for (uint32_t tid : runnable_thread_ids) {
+          ss << " " << tid;
+        }
+        ss << "\n";
+        Locks::mutator_lock_->Dump(ss);
+        ss << "\n";
+        runtime->GetThreadList()->Dump(ss);
+        LOG(FATAL) << ss.str();
+      }
+    } else {
+      barrier->Increment(self, barrier_count);
+    }
   }
   Locks::mutator_lock_->SharedLock(self);
 }
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 8ff5e5a..3ed138c 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -294,66 +294,15 @@
   }
 
   // Load image space(s).
-  if (!image_file_name.empty()) {
-    // For code reuse, handle this like a work queue.
-    std::vector<std::string> image_file_names;
-    image_file_names.push_back(image_file_name);
-    // The loaded spaces. Secondary images may fail to load, in which case we need to remove
-    // already added spaces.
-    std::vector<space::Space*> added_image_spaces;
-    uint8_t* const original_requested_alloc_space_begin = requested_alloc_space_begin;
-    for (size_t index = 0; index < image_file_names.size(); ++index) {
-      std::string& image_name = image_file_names[index];
-      std::string error_msg;
-      std::unique_ptr<space::ImageSpace> boot_image_space_uptr = space::ImageSpace::CreateBootImage(
-          image_name.c_str(),
-          image_instruction_set,
-          index > 0,
-          &error_msg);
-      if (boot_image_space_uptr != nullptr) {
-        space::ImageSpace* boot_image_space = boot_image_space_uptr.release();
-        AddSpace(boot_image_space);
-        added_image_spaces.push_back(boot_image_space);
-        // Oat files referenced by image files immediately follow them in memory, ensure alloc space
-        // isn't going to get in the middle
-        uint8_t* oat_file_end_addr = boot_image_space->GetImageHeader().GetOatFileEnd();
-        CHECK_GT(oat_file_end_addr, boot_image_space->End());
-        requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
-        boot_image_spaces_.push_back(boot_image_space);
-
-        if (index == 0) {
-          // If this was the first space, check whether there are more images to load.
-          const OatFile* boot_oat_file = boot_image_space->GetOatFile();
-          if (boot_oat_file == nullptr) {
-            continue;
-          }
-
-          const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader();
-          const char* boot_classpath =
-              boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
-          if (boot_classpath == nullptr) {
-            continue;
-          }
-
-          space::ImageSpace::ExtractMultiImageLocations(image_file_name,
-                                                        boot_classpath,
-                                                        &image_file_names);
-        }
-      } else {
-        LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
-            << "Attempting to fall back to imageless running. Error was: " << error_msg
-            << "\nAttempted image: " << image_name;
-        // Remove already loaded spaces.
-        for (space::Space* loaded_space : added_image_spaces) {
-          RemoveSpace(loaded_space);
-          delete loaded_space;
-        }
-        boot_image_spaces_.clear();
-        requested_alloc_space_begin = original_requested_alloc_space_begin;
-        break;
-      }
+  if (space::ImageSpace::LoadBootImage(image_file_name,
+                                       image_instruction_set,
+                                       &boot_image_spaces_,
+                                       &requested_alloc_space_begin)) {
+    for (auto space : boot_image_spaces_) {
+      AddSpace(space);
     }
   }
+
   /*
   requested_alloc_space_begin ->     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
                                      +-  nonmoving space (non_moving_space_capacity)+-
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 6019540..c726944 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1617,6 +1617,81 @@
   return nullptr;
 }
 
+bool ImageSpace::LoadBootImage(const std::string& image_file_name,
+                               const InstructionSet image_instruction_set,
+                               std::vector<space::ImageSpace*>* boot_image_spaces,
+                               uint8_t** oat_file_end) {
+  DCHECK(boot_image_spaces != nullptr);
+  DCHECK(boot_image_spaces->empty());
+  DCHECK(oat_file_end != nullptr);
+  DCHECK_NE(image_instruction_set, InstructionSet::kNone);
+
+  if (image_file_name.empty()) {
+    return false;
+  }
+
+  // For code reuse, handle this like a work queue.
+  std::vector<std::string> image_file_names;
+  image_file_names.push_back(image_file_name);
+
+  bool error = false;
+  uint8_t* oat_file_end_tmp = *oat_file_end;
+
+  for (size_t index = 0; index < image_file_names.size(); ++index) {
+    std::string& image_name = image_file_names[index];
+    std::string error_msg;
+    std::unique_ptr<space::ImageSpace> boot_image_space_uptr = CreateBootImage(
+        image_name.c_str(),
+        image_instruction_set,
+        index > 0,
+        &error_msg);
+    if (boot_image_space_uptr != nullptr) {
+      space::ImageSpace* boot_image_space = boot_image_space_uptr.release();
+      boot_image_spaces->push_back(boot_image_space);
+      // Oat files referenced by image files immediately follow them in memory, ensure alloc space
+      // isn't going to get in the middle
+      uint8_t* oat_file_end_addr = boot_image_space->GetImageHeader().GetOatFileEnd();
+      CHECK_GT(oat_file_end_addr, boot_image_space->End());
+      oat_file_end_tmp = AlignUp(oat_file_end_addr, kPageSize);
+
+      if (index == 0) {
+        // If this was the first space, check whether there are more images to load.
+        const OatFile* boot_oat_file = boot_image_space->GetOatFile();
+        if (boot_oat_file == nullptr) {
+          continue;
+        }
+
+        const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader();
+        const char* boot_classpath =
+            boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
+        if (boot_classpath == nullptr) {
+          continue;
+        }
+
+        ExtractMultiImageLocations(image_file_name, boot_classpath, &image_file_names);
+      }
+    } else {
+      error = true;
+      LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
+          << "Attempting to fall back to imageless running. Error was: " << error_msg
+          << "\nAttempted image: " << image_name;
+      break;
+    }
+  }
+
+  if (error) {
+    // Remove already loaded spaces.
+    for (space::Space* loaded_space : *boot_image_spaces) {
+      delete loaded_space;
+    }
+    boot_image_spaces->clear();
+    return false;
+  }
+
+  *oat_file_end = oat_file_end_tmp;
+  return true;
+}
+
 std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image,
                                                            const OatFile* oat_file,
                                                            std::string* error_msg) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 0ba131b..489a289 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -17,6 +17,7 @@
 #ifndef ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
 #define ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
 
+#include "arch/instruction_set.h"
 #include "gc/accounting/space_bitmap.h"
 #include "runtime.h"
 #include "space.h"
@@ -35,18 +36,15 @@
     return kSpaceTypeImageSpace;
   }
 
-  // Create a boot image space from an image file for a specified instruction
-  // set. Cannot be used for future allocation or collected.
+  // Load boot image spaces from a primary image file for a specified instruction set.
   //
-  // Create also opens the OatFile associated with the image file so
-  // that it be contiguously allocated with the image before the
-  // creation of the alloc space. The ReleaseOatFile will later be
-  // used to transfer ownership of the OatFile to the ClassLinker when
-  // it is initialized.
-  static std::unique_ptr<ImageSpace> CreateBootImage(const char* image,
-                                     InstructionSet image_isa,
-                                     bool secondary_image,
-                                     std::string* error_msg)
+  // On successful return, the loaded spaces are added to boot_image_spaces (which must be
+  // empty on entry) and oat_file_end is updated with the (page-aligned) end of the last
+  // oat file.
+  static bool LoadBootImage(const std::string& image_file_name,
+                            const InstructionSet image_instruction_set,
+                            std::vector<space::ImageSpace*>* boot_image_spaces,
+                            uint8_t** oat_file_end)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Try to open an existing app image space.
@@ -189,6 +187,20 @@
   friend class Space;
 
  private:
+  // Create a boot image space from an image file for a specified instruction
+  // set. Cannot be used for future allocation or collected.
+  //
+  // Create also opens the OatFile associated with the image file so
+  // that it be contiguously allocated with the image before the
+  // creation of the alloc space. The ReleaseOatFile will later be
+  // used to transfer ownership of the OatFile to the ClassLinker when
+  // it is initialized.
+  static std::unique_ptr<ImageSpace> CreateBootImage(const char* image,
+                                     InstructionSet image_isa,
+                                     bool secondary_image,
+                                     std::string* error_msg)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   DISALLOW_COPY_AND_ASSIGN(ImageSpace);
 };
 
diff --git a/runtime/interpreter/mterp/arm/entry.S b/runtime/interpreter/mterp/arm/entry.S
index a6b131d..e53c054 100644
--- a/runtime/interpreter/mterp/arm/entry.S
+++ b/runtime/interpreter/mterp/arm/entry.S
@@ -31,10 +31,19 @@
  *
  */
 
-ExecuteMterpImpl:
-    .fnstart
-    .save {r3-r10,fp,lr}
+ENTRY ExecuteMterpImpl
     stmfd   sp!, {r3-r10,fp,lr}         @ save 10 regs, (r3 just to align 64)
+    .cfi_adjust_cfa_offset 40
+    .cfi_rel_offset r3, 0
+    .cfi_rel_offset r4, 4
+    .cfi_rel_offset r5, 8
+    .cfi_rel_offset r6, 12
+    .cfi_rel_offset r7, 16
+    .cfi_rel_offset r8, 20
+    .cfi_rel_offset r9, 24
+    .cfi_rel_offset r10, 28
+    .cfi_rel_offset fp, 32
+    .cfi_rel_offset lr, 36
 
     /* Remember the return register */
     str     r3, [r2, #SHADOWFRAME_RESULT_REGISTER_OFFSET]
diff --git a/runtime/interpreter/mterp/arm/footer.S b/runtime/interpreter/mterp/arm/footer.S
index cd32ea2..c6801e5 100644
--- a/runtime/interpreter/mterp/arm/footer.S
+++ b/runtime/interpreter/mterp/arm/footer.S
@@ -294,6 +294,5 @@
     mov     r0, rINST                               @ restore return value
     ldmfd   sp!, {r3-r10,fp,pc}                     @ restore 10 regs and return
 
-    .fnend
-    .size   ExecuteMterpImpl, .-ExecuteMterpImpl
+    END ExecuteMterpImpl
 
diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S
index 039bcbe..597d9d4 100644
--- a/runtime/interpreter/mterp/arm/header.S
+++ b/runtime/interpreter/mterp/arm/header.S
@@ -287,3 +287,24 @@
 .macro REFRESH_IBASE
   ldr     rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET]
 .endm
+
+/*
+ * cfi support macros.
+ */
+.macro ENTRY name
+    .arm
+    .type \name, #function
+    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
+    .global \name
+    /* Cache alignment for function entry */
+    .balign 16
+\name:
+    .cfi_startproc
+    .fnstart
+.endm
+
+.macro END name
+    .fnend
+    .cfi_endproc
+    .size \name, .-\name
+.endm
diff --git a/runtime/interpreter/mterp/arm/op_double_to_long.S b/runtime/interpreter/mterp/arm/op_double_to_long.S
index b100810..19ff723 100644
--- a/runtime/interpreter/mterp/arm/op_double_to_long.S
+++ b/runtime/interpreter/mterp/arm/op_double_to_long.S
@@ -1,4 +1,3 @@
-@include "arm/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
 %include "arm/unopWide.S" {"instr":"bl      d2l_doconv"}
 
 %break
@@ -10,43 +9,25 @@
  * to modest integer.  The EABI convert function isn't doing this for us.
  */
 d2l_doconv:
-    stmfd   sp!, {r4, r5, lr}           @ save regs
-    mov     r3, #0x43000000             @ maxlong, as a double (high word)
-    add     r3, #0x00e00000             @  0x43e00000
-    mov     r2, #0                      @ maxlong, as a double (low word)
-    sub     sp, sp, #4                  @ align for EABI
-    mov     r4, r0                      @ save a copy of r0
-    mov     r5, r1                      @  and r1
-    bl      __aeabi_dcmpge              @ is arg >= maxlong?
-    cmp     r0, #0                      @ nonzero == yes
-    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
-    mvnne   r1, #0x80000000
-    bne     1f
-
-    mov     r0, r4                      @ recover arg
-    mov     r1, r5
-    mov     r3, #0xc3000000             @ minlong, as a double (high word)
-    add     r3, #0x00e00000             @  0xc3e00000
-    mov     r2, #0                      @ minlong, as a double (low word)
-    bl      __aeabi_dcmple              @ is arg <= minlong?
-    cmp     r0, #0                      @ nonzero == yes
-    movne   r0, #0                      @ return minlong (8000000000000000)
-    movne   r1, #0x80000000
-    bne     1f
-
-    mov     r0, r4                      @ recover arg
-    mov     r1, r5
-    mov     r2, r4                      @ compare against self
-    mov     r3, r5
-    bl      __aeabi_dcmpeq              @ is arg == self?
-    cmp     r0, #0                      @ zero == no
-    moveq   r1, #0                      @ return zero for NaN
-    beq     1f
-
-    mov     r0, r4                      @ recover arg
-    mov     r1, r5
-    bl      __aeabi_d2lz                @ convert double to long
-
-1:
-    add     sp, sp, #4
-    ldmfd   sp!, {r4, r5, pc}
+    ubfx    r2, r1, #20, #11            @ grab the exponent
+    movw    r3, #0x43e
+    cmp     r2, r3                      @ MINLONG < x > MAXLONG?
+    bhs     d2l_special_cases
+    b       __aeabi_d2lz                @ tail call to convert double to long
+d2l_special_cases:
+    movw    r3, #0x7ff
+    cmp     r2, r3
+    beq     d2l_maybeNaN                @ NaN?
+d2l_notNaN:
+    adds    r1, r1, r1                  @ sign bit to carry
+    mov     r0, #0xffffffff             @ assume maxlong for lsw
+    mov     r1, #0x7fffffff             @ assume maxlong for msw
+    adc     r0, r0, #0
+    adc     r1, r1, #0                  @ convert maxlong to minlong if exp negative
+    bx      lr                          @ return
+d2l_maybeNaN:
+    orrs    r3, r0, r1, lsl #12
+    beq     d2l_notNaN                  @ if fraction is non-zero, it's a NaN
+    mov     r0, #0
+    mov     r1, #0
+    bx      lr                          @ return 0 for NaN
diff --git a/runtime/interpreter/mterp/arm/op_float_to_long.S b/runtime/interpreter/mterp/arm/op_float_to_long.S
index 5c8680f..1770ea0 100644
--- a/runtime/interpreter/mterp/arm/op_float_to_long.S
+++ b/runtime/interpreter/mterp/arm/op_float_to_long.S
@@ -1,4 +1,3 @@
-@include "arm/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
 %include "arm/unopWider.S" {"instr":"bl      f2l_doconv"}
 
 %break
@@ -10,30 +9,23 @@
  * to modest integer.  The EABI convert function isn't doing this for us.
  */
 f2l_doconv:
-    stmfd   sp!, {r4, lr}
-    mov     r1, #0x5f000000             @ (float)maxlong
-    mov     r4, r0
-    bl      __aeabi_fcmpge              @ is arg >= maxlong?
-    cmp     r0, #0                      @ nonzero == yes
-    mvnne   r0, #0                      @ return maxlong (7fffffff)
-    mvnne   r1, #0x80000000
-    popne   {r4, pc}
-
-    mov     r0, r4                      @ recover arg
-    mov     r1, #0xdf000000             @ (float)minlong
-    bl      __aeabi_fcmple              @ is arg <= minlong?
-    cmp     r0, #0                      @ nonzero == yes
-    movne   r0, #0                      @ return minlong (80000000)
-    movne   r1, #0x80000000
-    popne   {r4, pc}
-
-    mov     r0, r4                      @ recover arg
-    mov     r1, r4
-    bl      __aeabi_fcmpeq              @ is arg == self?
-    cmp     r0, #0                      @ zero == no
-    moveq   r1, #0                      @ return zero for NaN
-    popeq   {r4, pc}
-
-    mov     r0, r4                      @ recover arg
-    bl      __aeabi_f2lz                @ convert float to long
-    ldmfd   sp!, {r4, pc}
+    ubfx    r2, r0, #23, #8             @ grab the exponent
+    cmp     r2, #0xbe                   @ MININT < x > MAXINT?
+    bhs     f2l_special_cases
+    b       __aeabi_f2lz                @ tail call to convert float to long
+f2l_special_cases:
+    cmp     r2, #0xff                   @ NaN or infinity?
+    beq     f2l_maybeNaN
+f2l_notNaN:
+    adds    r0, r0, r0                  @ sign bit to carry
+    mov     r0, #0xffffffff             @ assume maxlong for lsw
+    mov     r1, #0x7fffffff             @ assume maxlong for msw
+    adcs    r0, r0, #0                  @ convert maxlong to minlong if exp negative
+    adc     r1, r1, #0                  @ convert maxlong to minlong if exp negative
+    bx      lr                          @ return
+f2l_maybeNaN:
+    lsls    r3, r0, #9
+    beq     f2l_notNaN                  @ if fraction is non-zero, it's a NaN
+    mov     r0, #0
+    mov     r1, #0
+    bx      lr                          @ return 0 for NaN
diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S
index 4d540d7..c80d6b9 100644
--- a/runtime/interpreter/mterp/out/mterp_arm.S
+++ b/runtime/interpreter/mterp/out/mterp_arm.S
@@ -295,6 +295,27 @@
   ldr     rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET]
 .endm
 
+/*
+ * cfi support macros.
+ */
+.macro ENTRY name
+    .arm
+    .type \name, #function
+    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
+    .global \name
+    /* Cache alignment for function entry */
+    .balign 16
+\name:
+    .cfi_startproc
+    .fnstart
+.endm
+
+.macro END name
+    .fnend
+    .cfi_endproc
+    .size \name, .-\name
+.endm
+
 /* File: arm/entry.S */
 /*
  * Copyright (C) 2016 The Android Open Source Project
@@ -329,10 +350,19 @@
  *
  */
 
-ExecuteMterpImpl:
-    .fnstart
-    .save {r3-r10,fp,lr}
+ENTRY ExecuteMterpImpl
     stmfd   sp!, {r3-r10,fp,lr}         @ save 10 regs, (r3 just to align 64)
+    .cfi_adjust_cfa_offset 40
+    .cfi_rel_offset r3, 0
+    .cfi_rel_offset r4, 4
+    .cfi_rel_offset r5, 8
+    .cfi_rel_offset r6, 12
+    .cfi_rel_offset r7, 16
+    .cfi_rel_offset r8, 20
+    .cfi_rel_offset r9, 24
+    .cfi_rel_offset r10, 28
+    .cfi_rel_offset fp, 32
+    .cfi_rel_offset lr, 36
 
     /* Remember the return register */
     str     r3, [r2, #SHADOWFRAME_RESULT_REGISTER_OFFSET]
@@ -3664,7 +3694,6 @@
     .balign 128
 .L_op_float_to_long: /* 0x88 */
 /* File: arm/op_float_to_long.S */
-@include "arm/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
 /* File: arm/unopWider.S */
     /*
      * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
@@ -3742,7 +3771,6 @@
     .balign 128
 .L_op_double_to_long: /* 0x8b */
 /* File: arm/op_double_to_long.S */
-@include "arm/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
 /* File: arm/unopWide.S */
     /*
      * Generic 64-bit unary operation.  Provide an "instr" line that
@@ -7386,33 +7414,26 @@
  * to modest integer.  The EABI convert function isn't doing this for us.
  */
 f2l_doconv:
-    stmfd   sp!, {r4, lr}
-    mov     r1, #0x5f000000             @ (float)maxlong
-    mov     r4, r0
-    bl      __aeabi_fcmpge              @ is arg >= maxlong?
-    cmp     r0, #0                      @ nonzero == yes
-    mvnne   r0, #0                      @ return maxlong (7fffffff)
-    mvnne   r1, #0x80000000
-    popne   {r4, pc}
-
-    mov     r0, r4                      @ recover arg
-    mov     r1, #0xdf000000             @ (float)minlong
-    bl      __aeabi_fcmple              @ is arg <= minlong?
-    cmp     r0, #0                      @ nonzero == yes
-    movne   r0, #0                      @ return minlong (80000000)
-    movne   r1, #0x80000000
-    popne   {r4, pc}
-
-    mov     r0, r4                      @ recover arg
-    mov     r1, r4
-    bl      __aeabi_fcmpeq              @ is arg == self?
-    cmp     r0, #0                      @ zero == no
-    moveq   r1, #0                      @ return zero for NaN
-    popeq   {r4, pc}
-
-    mov     r0, r4                      @ recover arg
-    bl      __aeabi_f2lz                @ convert float to long
-    ldmfd   sp!, {r4, pc}
+    ubfx    r2, r0, #23, #8             @ grab the exponent
+    cmp     r2, #0xbe                   @ MININT < x > MAXINT?
+    bhs     f2l_special_cases
+    b       __aeabi_f2lz                @ tail call to convert float to long
+f2l_special_cases:
+    cmp     r2, #0xff                   @ NaN or infinity?
+    beq     f2l_maybeNaN
+f2l_notNaN:
+    adds    r0, r0, r0                  @ sign bit to carry
+    mov     r0, #0xffffffff             @ assume maxlong for lsw
+    mov     r1, #0x7fffffff             @ assume maxlong for msw
+    adcs    r0, r0, #0                  @ convert maxlong to minlong if exp negative
+    adc     r1, r1, #0                  @ convert maxlong to minlong if exp negative
+    bx      lr                          @ return
+f2l_maybeNaN:
+    lsls    r3, r0, #9
+    beq     f2l_notNaN                  @ if fraction is non-zero, it's a NaN
+    mov     r0, #0
+    mov     r1, #0
+    bx      lr                          @ return 0 for NaN
 
 /* continuation for op_double_to_long */
 /*
@@ -7423,46 +7444,28 @@
  * to modest integer.  The EABI convert function isn't doing this for us.
  */
 d2l_doconv:
-    stmfd   sp!, {r4, r5, lr}           @ save regs
-    mov     r3, #0x43000000             @ maxlong, as a double (high word)
-    add     r3, #0x00e00000             @  0x43e00000
-    mov     r2, #0                      @ maxlong, as a double (low word)
-    sub     sp, sp, #4                  @ align for EABI
-    mov     r4, r0                      @ save a copy of r0
-    mov     r5, r1                      @  and r1
-    bl      __aeabi_dcmpge              @ is arg >= maxlong?
-    cmp     r0, #0                      @ nonzero == yes
-    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
-    mvnne   r1, #0x80000000
-    bne     1f
-
-    mov     r0, r4                      @ recover arg
-    mov     r1, r5
-    mov     r3, #0xc3000000             @ minlong, as a double (high word)
-    add     r3, #0x00e00000             @  0xc3e00000
-    mov     r2, #0                      @ minlong, as a double (low word)
-    bl      __aeabi_dcmple              @ is arg <= minlong?
-    cmp     r0, #0                      @ nonzero == yes
-    movne   r0, #0                      @ return minlong (8000000000000000)
-    movne   r1, #0x80000000
-    bne     1f
-
-    mov     r0, r4                      @ recover arg
-    mov     r1, r5
-    mov     r2, r4                      @ compare against self
-    mov     r3, r5
-    bl      __aeabi_dcmpeq              @ is arg == self?
-    cmp     r0, #0                      @ zero == no
-    moveq   r1, #0                      @ return zero for NaN
-    beq     1f
-
-    mov     r0, r4                      @ recover arg
-    mov     r1, r5
-    bl      __aeabi_d2lz                @ convert double to long
-
-1:
-    add     sp, sp, #4
-    ldmfd   sp!, {r4, r5, pc}
+    ubfx    r2, r1, #20, #11            @ grab the exponent
+    movw    r3, #0x43e
+    cmp     r2, r3                      @ MINLONG < x > MAXLONG?
+    bhs     d2l_special_cases
+    b       __aeabi_d2lz                @ tail call to convert double to long
+d2l_special_cases:
+    movw    r3, #0x7ff
+    cmp     r2, r3
+    beq     d2l_maybeNaN                @ NaN?
+d2l_notNaN:
+    adds    r1, r1, r1                  @ sign bit to carry
+    mov     r0, #0xffffffff             @ assume maxlong for lsw
+    mov     r1, #0x7fffffff             @ assume maxlong for msw
+    adcs    r0, r0, #0
+    adc     r1, r1, #0                  @ convert maxlong to minlong if exp negative
+    bx      lr                          @ return
+d2l_maybeNaN:
+    orrs    r3, r0, r1, lsl #12
+    beq     d2l_notNaN                  @ if fraction is non-zero, it's a NaN
+    mov     r0, #0
+    mov     r1, #0
+    bx      lr                          @ return 0 for NaN
 
     .size   artMterpAsmSisterStart, .-artMterpAsmSisterStart
     .global artMterpAsmSisterEnd
@@ -12115,6 +12118,17 @@
     cmp     rPROFILE, #0
     bgt     MterpProfileActive                      @ if > 0, we may have some counts to report.
     ldmfd   sp!, {r3-r10,fp,pc}                     @ restore 10 regs and return
+    .cfi_restore r3
+    .cfi_restore r4
+    .cfi_restore r5
+    .cfi_restore r6
+    .cfi_restore r7
+    .cfi_restore r9
+    .cfi_restore r9
+    .cfi_restore r10
+    .cfi_restore fp
+    .cfi_restore pc
+    .cfi_adjust_cfa_offset -40
 
 MterpProfileActive:
     mov     rINST, r0                               @ stash return value
@@ -12126,8 +12140,18 @@
     bl      MterpAddHotnessBatch                    @ (method, shadow_frame, self)
     mov     r0, rINST                               @ restore return value
     ldmfd   sp!, {r3-r10,fp,pc}                     @ restore 10 regs and return
+    .cfi_restore r3
+    .cfi_restore r4
+    .cfi_restore r5
+    .cfi_restore r6
+    .cfi_restore r7
+    .cfi_restore r9
+    .cfi_restore r9
+    .cfi_restore r10
+    .cfi_restore fp
+    .cfi_restore pc
+    .cfi_adjust_cfa_offset -40
 
-    .fnend
-    .size   ExecuteMterpImpl, .-ExecuteMterpImpl
+    END ExecuteMterpImpl
 
 
diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc
index 259bbbe..7c6a710 100644
--- a/runtime/mirror/class_ext.cc
+++ b/runtime/mirror/class_ext.cc
@@ -46,8 +46,9 @@
   SetFieldObject<false>(obsolete_methods_off, methods.Ptr());
 }
 
-// TODO We really need to be careful how we update this. If we ever in the future make it so that
-// these arrays are written into without all threads being suspended we have a race condition!
+// We really need to be careful how we update this. If we ever in the future make it so that
+// these arrays are written into without all threads being suspended we have a race condition! This
+// race could cause obsolete methods to be missed.
 bool ClassExt::ExtendObsoleteArrays(Thread* self, uint32_t increase) {
   DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId())
       << "Obsolete arrays are set without synchronization!";
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 8a01043..ae6b31d 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -67,7 +67,7 @@
 
 // Set by the verifier for a method that could not be verified to follow structured locking.
 static constexpr uint32_t kAccMustCountLocks =        0x02000000;  // method (runtime)
-// Set to indicate that the ArtMethod is obsolete and has a different DexCache from it's declaring
+// Set to indicate that the ArtMethod is obsolete and has a different DexCache from its declaring
 // class.
 // TODO Might want to re-arrange some of these so that we can have obsolete + intrinsic methods.
 static constexpr uint32_t kAccObsoleteMethod =        0x04000000;  // method (runtime)
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 69bd887..d0349b9 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -66,7 +66,8 @@
     return map;
   }
   memcpy(map->Begin(), dex_data, data_len);
-  // Make the dex files mmap read only.
+  // Make the dex files mmap read only. This matches how other DexFiles are mmaped and prevents
+  // programs from corrupting it.
   map->Protect(PROT_READ);
   return map;
 }
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index f3a5834..c819acd 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -68,7 +68,7 @@
  public:
   // Redefine the given class with the given dex data. Note this function does not take ownership of
   // the dex_data pointer. It is not used after this call however and may be freed if desired.
-  // The caller is responsible for freeing it. The runtime makes it's own copy of the data.
+  // The caller is responsible for freeing it. The runtime makes its own copy of the data.
   static jvmtiError RedefineClass(ArtJvmTiEnv* env,
                                   art::Runtime* runtime,
                                   art::Thread* self,
@@ -146,6 +146,7 @@
   // This will check that no constraints are violated (more than 1 class in dex file, any changes in
   // number/declaration of methods & fields, changes in access flags, etc.)
   bool EnsureRedefinitionIsValid() {
+    LOG(WARNING) << "Redefinition is not checked for validity currently";
     return true;
   }
 
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 1283cf0..17f5513 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1394,6 +1394,7 @@
     os << "  | group=\"" << group_name << "\""
        << " sCount=" << thread->tls32_.suspend_count
        << " dsCount=" << thread->tls32_.debug_suspend_count
+       << " flags=" << thread->tls32_.state_and_flags.as_struct.flags
        << " obj=" << reinterpret_cast<void*>(thread->tlsPtr_.opeer)
        << " self=" << reinterpret_cast<const void*>(thread) << "\n";
   }
@@ -3175,4 +3176,8 @@
   tlsPtr_.exception = new_exception.Ptr();
 }
 
+bool Thread::IsAotCompiler() {
+  return Runtime::Current()->IsAotCompiler();
+}
+
 }  // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index 35226f2..b80fdc7 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -33,13 +33,13 @@
 #include "base/mutex.h"
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints.h"
+#include "gc_root.h"
 #include "globals.h"
 #include "handle_scope.h"
 #include "instrumentation.h"
 #include "jvalue.h"
 #include "object_callbacks.h"
 #include "offsets.h"
-#include "runtime.h"
 #include "runtime_stats.h"
 #include "stack.h"
 #include "thread_state.h"
@@ -87,7 +87,6 @@
 class JavaVMExt;
 struct JNIEnvExt;
 class Monitor;
-class Runtime;
 class ScopedObjectAccessAlreadyRunnable;
 class ShadowFrame;
 class SingleStepControl;
@@ -949,17 +948,17 @@
   }
 
   std::vector<ArtMethod*>* GetStackTraceSample() const {
-    DCHECK(!Runtime::Current()->IsAotCompiler());
+    DCHECK(!IsAotCompiler());
     return tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample;
   }
 
   void SetStackTraceSample(std::vector<ArtMethod*>* sample) {
-    DCHECK(!Runtime::Current()->IsAotCompiler());
+    DCHECK(!IsAotCompiler());
     tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample = sample;
   }
 
   verifier::VerifierDeps* GetVerifierDeps() const {
-    DCHECK(Runtime::Current()->IsAotCompiler());
+    DCHECK(IsAotCompiler());
     return tlsPtr_.deps_or_stack_trace_sample.verifier_deps;
   }
 
@@ -967,7 +966,7 @@
   // entry in the thread is cleared before destruction of the actual VerifierDeps
   // object, or the thread.
   void SetVerifierDeps(verifier::VerifierDeps* verifier_deps) {
-    DCHECK(Runtime::Current()->IsAotCompiler());
+    DCHECK(IsAotCompiler());
     DCHECK(verifier_deps == nullptr || tlsPtr_.deps_or_stack_trace_sample.verifier_deps == nullptr);
     tlsPtr_.deps_or_stack_trace_sample.verifier_deps = verifier_deps;
   }
@@ -1246,6 +1245,8 @@
   // Install the protected region for implicit stack checks.
   void InstallImplicitProtection();
 
+  static bool IsAotCompiler();
+
   // 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to
   // change from being Suspended to Runnable without a suspend request occurring.
   union PACKED(4) StateAndFlags {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 27fb37a..a6bd83d 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -375,7 +375,7 @@
   return count;
 }
 
-size_t ThreadList::RunEmptyCheckpoint() {
+size_t ThreadList::RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids) {
   Thread* self = Thread::Current();
   Locks::mutator_lock_->AssertNotExclusiveHeld(self);
   Locks::thread_list_lock_->AssertNotHeld(self);
@@ -392,6 +392,9 @@
             // This thread will run an empty checkpoint (decrement the empty checkpoint barrier)
             // some time in the near future.
             ++count;
+            if (kIsDebugBuild) {
+              runnable_thread_ids.push_back(thread->GetThreadId());
+            }
             break;
           }
           if (thread->GetState() != kRunnable) {
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 133d430..1acabcb 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -27,6 +27,7 @@
 
 #include <bitset>
 #include <list>
+#include <vector>
 
 namespace art {
 namespace gc {
@@ -106,7 +107,9 @@
   // in-flight mutator heap access (eg. a read barrier.) Runnable threads will respond by
   // decrementing the empty checkpoint barrier count. This works even when the weak ref access is
   // disabled. Only one concurrent use is currently supported.
-  size_t RunEmptyCheckpoint()
+  // In debug build, runnable_thread_ids will be populated with the thread IDS of the runnable
+  // thread to wait for.
+  size_t RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids)
       REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
 
   size_t RunCheckpointOnRunnableThreads(Closure* checkpoint_function)
diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java
index 146f309..7754b75 100644
--- a/test/422-type-conversion/src/Main.java
+++ b/test/422-type-conversion/src/Main.java
@@ -390,6 +390,8 @@
     assertLongEquals(9223372036854775807L, $opt$noinline$FloatToLong(9223372036854775807F));  // 2^63 - 1
     assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(-9223372036854775807F));  // -(2^63 - 1)
     assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(-9223372036854775808F));  // -(2^63)
+    assertLongEquals(9223371487098961920L, $opt$noinline$FloatToLong(9223371487098961920F));  // Math.nextAfter(2F^63, 0)
+    assertLongEquals(-9223371487098961920L, $opt$noinline$FloatToLong(-9223371487098961920F));  // Math.nextAfter(-2F^63, 0)
     assertLongEquals(0L, $opt$noinline$FloatToLong(Float.NaN));
     assertLongEquals(9223372036854775807L, $opt$noinline$FloatToLong(Float.POSITIVE_INFINITY));
     assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(Float.NEGATIVE_INFINITY));
@@ -469,6 +471,8 @@
     assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(-9223372036854775807D));  // -(2^63 - 1)
     assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(-9223372036854775808D));  // -(2^63)
     assertLongEquals(0L, $opt$noinline$DoubleToLong(Double.NaN));
+    assertLongEquals(9223372036854774784L, $opt$noinline$DoubleToLong(9223372036854774784D));  // Math.nextAfter(2D^63, 0)
+    assertLongEquals(-9223372036854774784L, $opt$noinline$DoubleToLong(-9223372036854774784D));  // Math.nextAfter(-2D^63, 0)
     assertLongEquals(9223372036854775807L, $opt$noinline$DoubleToLong(Double.POSITIVE_INFINITY));
     assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(Double.NEGATIVE_INFINITY));
   }
diff --git a/test/530-checker-loops1/info.txt b/test/530-checker-loops1/info.txt
index f5d334d..ecefa7e 100644
--- a/test/530-checker-loops1/info.txt
+++ b/test/530-checker-loops1/info.txt
@@ -1 +1 @@
-Test on loop optimizations.
+Test on loop optimizations, in particular around common induction.
diff --git a/test/530-checker-loops1/src/Main.java b/test/530-checker-loops1/src/Main.java
index dde4d62..383c28f 100644
--- a/test/530-checker-loops1/src/Main.java
+++ b/test/530-checker-loops1/src/Main.java
@@ -15,7 +15,7 @@
  */
 
 //
-// Test on loop optimizations.
+// Test on loop optimizations, in particular around common induction.
 //
 public class Main {
 
diff --git a/test/530-checker-loops2/info.txt b/test/530-checker-loops2/info.txt
index f5d334d..3b5a7ad 100644
--- a/test/530-checker-loops2/info.txt
+++ b/test/530-checker-loops2/info.txt
@@ -1 +1 @@
-Test on loop optimizations.
+Test on loop optimizations, in particular around less common induction.
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
index 47b6475..a94d69b 100644
--- a/test/530-checker-loops2/src/Main.java
+++ b/test/530-checker-loops2/src/Main.java
@@ -15,7 +15,7 @@
  */
 
 //
-// Test on loop optimizations.
+// Test on loop optimizations, in particular around less common induction.
 //
 public class Main {
 
diff --git a/test/530-checker-loops3/info.txt b/test/530-checker-loops3/info.txt
index 07d99a3..e262f8e 100644
--- a/test/530-checker-loops3/info.txt
+++ b/test/530-checker-loops3/info.txt
@@ -1 +1 @@
-Test on loop optimizations, in particular loop-based dynamic bce.
+Test on loop optimizations, in particular around loop-based dynamic bce.
diff --git a/test/530-checker-loops4/expected.txt b/test/530-checker-loops4/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/530-checker-loops4/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/530-checker-loops4/info.txt b/test/530-checker-loops4/info.txt
new file mode 100644
index 0000000..10cf3b1
--- /dev/null
+++ b/test/530-checker-loops4/info.txt
@@ -0,0 +1 @@
+Test on loop optimizations, in particular with geometric induction.
diff --git a/test/530-checker-loops4/src/Main.java b/test/530-checker-loops4/src/Main.java
new file mode 100644
index 0000000..2e19c88
--- /dev/null
+++ b/test/530-checker-loops4/src/Main.java
@@ -0,0 +1,323 @@
+/*
+ * 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.
+ */
+
+//
+// Test on loop optimizations, in particular around geometric induction.
+//
+public class Main {
+
+  /// CHECK-START: int Main.geo1(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Mul loop:<<Loop>>
+  //
+  /// CHECK-START: int Main.geo1(int) loop_optimization (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue         loop:none
+  /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0          loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant 1410065408 loop:none
+  /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Par>>,<<Int>>]  loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add [<<Mul>>,<<Zer>>]  loop:none
+  /// CHECK-DAG:              Return [<<Add>>]       loop:none
+  //
+  /// CHECK-START: int Main.geo1(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  public static int geo1(int a) {
+    for (int i = 0; i < 10; i++) {
+      a *= 10;
+    }
+    return a;
+  }
+
+  /// CHECK-START: int Main.geo2(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shl loop:<<Loop>>
+  //
+  /// CHECK-START: int Main.geo2(int) loop_optimization (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue        loop:none
+  /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0         loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant 1024      loop:none
+  /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Par>>,<<Int>>] loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add [<<Mul>>,<<Zer>>] loop:none
+  /// CHECK-DAG:              Return [<<Add>>]      loop:none
+  //
+  /// CHECK-START: int Main.geo2(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  public static int geo2(int a) {
+    for (int i = 0; i < 10; i++) {
+      a <<= 1;
+    }
+    return a;
+  }
+
+  /// CHECK-START: int Main.geo3(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Div loop:<<Loop>>
+  //
+  /// CHECK-START: int Main.geo3(int) loop_optimization (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue        loop:none
+  /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0         loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant 59049     loop:none
+  /// CHECK-DAG: <<Div:i\d+>> Div [<<Par>>,<<Int>>] loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add [<<Div>>,<<Zer>>] loop:none
+  /// CHECK-DAG:              Return [<<Add>>]      loop:none
+  //
+  /// CHECK-START: int Main.geo3(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  public static int geo3(int a) {
+    for (int i = 0; i < 10; i++) {
+      a /= 3;
+    }
+    return a;
+  }
+
+  /// CHECK-START: int Main.geo4(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Rem loop:<<Loop>>
+  //
+  /// CHECK-START: int Main.geo4(int) loop_optimization (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue        loop:none
+  /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0         loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant 7         loop:none
+  /// CHECK-DAG: <<Rem:i\d+>> Rem [<<Par>>,<<Int>>] loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add [<<Rem>>,<<Zer>>] loop:none
+  /// CHECK-DAG:              Return [<<Add>>]      loop:none
+  //
+  /// CHECK-START: int Main.geo4(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  public static int geo4(int a) {
+    for (int i = 0; i < 10; i++) {
+      a %= 7;
+    }
+    return a;
+  }
+
+  // TODO: someday?
+  public static int geo1BCE() {
+    int[] x = {  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
+                11, 12, 13, 14, 15, 16, 17, 19, 19, 20,
+                21, 22, 23, 24, 25, 26 };
+    int a = 1;
+    int r = 0;
+    for (int i = 0; i < 3; i++) {
+      r += x[a];
+      a *= 5;
+    }
+    return r;
+  }
+
+  // TODO: someday?
+  public static int geo2BCE() {
+    int[] x = {  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
+                11, 12, 13, 14, 15, 16, 17, 19, 19, 20,
+                21, 22, 23, 24, 25, 26 };
+    int a = 1;
+    int r = 0;
+    for (int i = 0; i < 5; i++) {
+      r += x[a];
+      a <<= 1;
+    }
+    return r;
+  }
+
+  /// CHECK-START: int Main.geo3BCE() BCE (before)
+  /// CHECK-DAG: BoundsCheck loop:none
+  /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+  //
+  /// CHECK-START: int Main.geo3BCE() BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  /// CHECK-NOT: Deoptimize
+  public static int geo3BCE() {
+    int[] x = {  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
+                11, 12, 13, 14, 15, 16, 17, 19, 19, 20,
+                21, 22, 23, 24, 25, 26 };
+    int a = 25;
+    int r = 0;
+    for (int i = 0; i < 100; i++) {  // a converges to 0
+      r += x[a];
+      a /= 5;
+    }
+    return r;
+  }
+
+  /// CHECK-START: int Main.geo4BCE() BCE (before)
+  /// CHECK-DAG: BoundsCheck loop:none
+  /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+  //
+  /// CHECK-START: int Main.geo4BCE() BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  /// CHECK-NOT: Deoptimize
+  public static int geo4BCE() {
+    int[] x = {  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
+                11, 12, 13, 14, 15, 16, 17, 19, 19, 20,
+                21, 22, 23, 24, 25, 26 };
+    int a = 25;
+    int r = 0;
+    for (int i = 0; i < 100; i++) {  // a converges to 0
+      r += x[a];
+      a %= 5;
+    }
+    return r;
+  }
+
+  /// CHECK-START: int Main.geoMulBlackHole(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Mul loop:<<Loop>>
+  //
+  /// CHECK-START: int Main.geoMulBlackHole(int) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+  /// CHECK-DAG:               Return [<<Zero>>]
+  //
+  /// CHECK-START: int Main.geoMulBlackHole(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  /// CHECK-NOT: Mul
+  public static int geoMulBlackHole(int a) {
+    for (int i = 0; i < 100; i++) {
+      a *= 10;
+    }
+    return a;
+  }
+
+  /// CHECK-START: int Main.geoShlBlackHole(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shl loop:<<Loop>>
+  //
+  /// CHECK-START: int Main.geoShlBlackHole(int) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+  /// CHECK-DAG:               Return [<<Zero>>]
+  //
+  /// CHECK-START: int Main.geoShlBlackHole(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  /// CHECK-NOT: Shl
+  public static int geoShlBlackHole(int a) {
+    for (int i = 0; i < 100; i++) {
+      a <<= 1;
+    }
+    return a;
+  }
+
+  /// CHECK-START: int Main.geoDivBlackHole(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Div loop:<<Loop>>
+  //
+  /// CHECK-START: int Main.geoDivBlackHole(int) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+  /// CHECK-DAG:               Return [<<Zero>>]
+  //
+  /// CHECK-START: int Main.geoDivBlackHole(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  /// CHECK-NOT: Div
+  public static int geoDivBlackHole(int a) {
+    for (int i = 0; i < 100; i++) {
+      a /= 10;
+    }
+    return a;
+  }
+
+  // TODO: Rem is already optimized away by the time the loop optimizer runs;
+  //       we could still optimize this case with last value on wrap-around!
+  //
+  /// CHECK-START: int Main.geoRemBlackHole(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Phi loop:<<Loop>>
+  //
+  /// CHECK-START: int Main.geoRemBlackHole(int) loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Phi loop:<<Loop>>
+  public static int geoRemBlackHole(int a) {
+    for (int i = 0; i < 100; i++) {
+      a %= 1;
+    }
+    return a;
+  }
+
+  //
+  // Verifier.
+  //
+
+  public static void main(String[] args) {
+    int m = 1410065408;
+    for (int i = -100; i <= 100; i++) {
+      expectEquals(m * i, geo1(i));
+    }
+    for (int i = 1; i <= 1000000000; i *= 10) {
+      expectEquals( m * i, geo1( i));
+      expectEquals(-m * i, geo1(-i));
+    }
+
+    for (int i = -100; i <= 100; i++) {
+      expectEquals(i << 10, geo2(i));
+    }
+    for (int i = 0; i < 22; i++) {
+      expectEquals(1 << (i + 10), geo2(1 << i));
+    }
+    expectEquals(0x80000400, geo2(0x00200001));
+    expectEquals(0x00000000, geo2(0x00400000));
+    expectEquals(0x00000400, geo2(0x00400001));
+
+    int d = 59049;
+    for (int i = -100; i <= 100; i++) {
+      expectEquals(0, geo3(i));
+    }
+    for (int i = 1; i <= 100; i++) {
+      expectEquals( i, geo3( i * d));
+      expectEquals( i, geo3( i * d + 1));
+      expectEquals(-i, geo3(-i * d));
+      expectEquals(-i, geo3(-i * d - 1));
+    }
+
+    for (int i = -100; i <= 100; i++) {
+      expectEquals(i % 7, geo4(i));
+    }
+
+    expectEquals(34,  geo1BCE());
+    expectEquals(36,  geo2BCE());
+    expectEquals(131, geo3BCE());
+    expectEquals(125, geo4BCE());
+
+    // Nothing escapes!
+    expectEquals(0, geoMulBlackHole(0));
+    expectEquals(0, geoShlBlackHole(0));
+    expectEquals(0, geoDivBlackHole(0));
+    expectEquals(0, geoRemBlackHole(0));
+    for (int i = -100; i <= 100; i++) {
+      expectEquals(0, geoMulBlackHole(i));
+      expectEquals(0, geoShlBlackHole(i));
+      expectEquals(0, geoDivBlackHole(i));
+      expectEquals(0, geoRemBlackHole(i));
+    }
+    for (int i = 0; i < 31; i++) {
+      expectEquals(0, geoMulBlackHole(1 << i));
+      expectEquals(0, geoShlBlackHole(1 << i));
+      expectEquals(0, geoDivBlackHole(1 << i));
+      expectEquals(0, geoRemBlackHole(1 << i));
+    }
+    expectEquals(0, geoMulBlackHole(0x7fffffff));
+    expectEquals(0, geoShlBlackHole(0x7fffffff));
+    expectEquals(0, geoDivBlackHole(0x7fffffff));
+    expectEquals(0, geoRemBlackHole(0x7fffffff));
+    expectEquals(0, geoMulBlackHole(0x80000000));
+    expectEquals(0, geoShlBlackHole(0x80000000));
+    expectEquals(0, geoDivBlackHole(0x80000000));
+    expectEquals(0, geoRemBlackHole(0x80000000));
+
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+}
diff --git a/test/629-vdex-speed/expected.txt b/test/629-vdex-speed/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/629-vdex-speed/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/629-vdex-speed/info.txt b/test/629-vdex-speed/info.txt
new file mode 100644
index 0000000..6d84cb5
--- /dev/null
+++ b/test/629-vdex-speed/info.txt
@@ -0,0 +1,2 @@
+Regression test for vdex that used to not AOT compile
+methods when the VerifierDeps were verified.
diff --git a/test/629-vdex-speed/run b/test/629-vdex-speed/run
new file mode 100644
index 0000000..f1b0a95
--- /dev/null
+++ b/test/629-vdex-speed/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+exec ${RUN} --vdex "${@}"
diff --git a/test/629-vdex-speed/src/Main.java b/test/629-vdex-speed/src/Main.java
new file mode 100644
index 0000000..470565a
--- /dev/null
+++ b/test/629-vdex-speed/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+    if (!isAotCompiled(Main.class, "main")) {
+      throw new Error("Expected Main.main to be AOT compiled");
+    }
+  }
+
+  private native static boolean isAotCompiled(Class<?> cls, String methodName);
+}
+
diff --git a/test/Android.arm_vixl.mk b/test/Android.arm_vixl.mk
index 5ae961a..c89eb4a 100644
--- a/test/Android.arm_vixl.mk
+++ b/test/Android.arm_vixl.mk
@@ -16,6 +16,5 @@
 
 # Known broken tests for the ARM VIXL backend.
 TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
-  488-checker-inline-recursive-calls \
-  552-checker-sharpening \
   562-checker-no-intermediate \
+  624-checker-stringops \
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index d15d016..28e1e60 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -360,8 +360,10 @@
 TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
 
 # Temporarily disable some broken tests when forcing access checks in interpreter b/22414682
+# 629 requires compilation.
 TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS := \
-  137-cfi
+  137-cfi \
+  629-vdex-speed
 
 ifneq (,$(filter interp-ac,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -425,6 +427,7 @@
 # explicitly test for them. These all also assume we have an image.
 # 147-stripped-dex-fallback is disabled because it requires --prebuild.
 # 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save.
+# 629-vdex-speed requires compiled code.
 TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \
   116-nodex2oat \
   117-nopatchoat \
@@ -433,7 +436,8 @@
   137-cfi \
   138-duplicate-classes-check2 \
   147-stripped-dex-fallback \
-  554-jit-profile-file
+  554-jit-profile-file \
+  629-vdex-speed
 
 # This test fails without an image.
 TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
@@ -470,12 +474,14 @@
 # This test dynamically enables tracing to force a deoptimization. This makes the test meaningless
 # when already tracing, and writes an error message that we do not want to check for.
 # 130 occasional timeout b/32383962.
+# 629 requires compilation.
 TEST_ART_BROKEN_TRACING_RUN_TESTS := \
   087-gc-after-link \
   130-hprof \
   137-cfi \
   141-class-unload \
   570-checker-osr \
+  629-vdex-speed \
   802-deoptimization
 
 ifneq (,$(filter trace stream,$(TRACE_TYPES)))
@@ -486,9 +492,11 @@
 
 # Known broken tests for the interpreter.
 # CFI unwinding expects managed frames.
+# 629 requires compilation.
 TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
   137-cfi \
-  554-jit-profile-file
+  554-jit-profile-file \
+  629-vdex-speed
 
 ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -504,8 +512,10 @@
 # Test 906 iterates the heap filtering with different options. No instances should be created
 # between those runs to be able to have precise checks.
 # Test 902 hits races with the JIT compiler. b/32821077
+# Test 629 requires compilation.
 TEST_ART_BROKEN_JIT_RUN_TESTS := \
   137-cfi \
+  629-vdex-speed \
   902-hello-transformation \
   904-object-allocation \
   906-iterate-heap \
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 9cfa324..285f3aa 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -119,6 +119,24 @@
   return JNI_TRUE;
 }
 
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env,
+                                                              jclass,
+                                                              jclass cls,
+                                                              jstring method_name) {
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+  ScopedUtfChars chars(env, method_name);
+  CHECK(chars.c_str() != nullptr);
+  ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
+        chars.c_str(), kRuntimePointerSize);
+  const void* code = method->GetOatMethodQuickCode(kRuntimePointerSize);
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) {
+    return true;
+  }
+  return code != nullptr;
+}
+
 extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
                                                              jclass,
                                                              jclass cls,
diff --git a/test/etc/default-build b/test/etc/default-build
index e663496..f2b5078 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -71,7 +71,7 @@
 declare -A JACK_EXPERIMENTAL_ARGS
 JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
 JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
-JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=26"
+JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b1"
 
 declare -A SMALI_EXPERIMENTAL_ARGS
 SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24"