Merge "Fix arm64 and x86_64 code generator assertion"
diff --git a/compiler/Android.bp b/compiler/Android.bp
index d57f301..b444fff 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -391,6 +391,7 @@
         mips64: {
             srcs: [
                 "linker/mips64/relative_patcher_mips64_test.cc",
+                "utils/mips64/managed_register_mips64_test.cc",
             ],
         },
         x86: {
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 808e28c..538fe93 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -70,10 +70,6 @@
     return *unit_.GetDexFile();
   }
 
-  bool PerformOptimizations() const {
-    return dex_to_dex_compilation_level_ >= DexToDexCompilationLevel::kOptimize;
-  }
-
   // Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where
   // a barrier is required.
   void CompileReturnVoid(Instruction* inst, uint32_t dex_pc);
@@ -114,7 +110,7 @@
 };
 
 void DexCompiler::Compile() {
-  DCHECK_GE(dex_to_dex_compilation_level_, DexToDexCompilationLevel::kRequired);
+  DCHECK_EQ(dex_to_dex_compilation_level_, DexToDexCompilationLevel::kOptimize);
   const DexFile::CodeItem* code_item = unit_.GetCodeItem();
   const uint16_t* insns = code_item->insns_;
   const uint32_t insns_size = code_item->insns_size_in_code_units_;
@@ -221,7 +217,7 @@
 }
 
 Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) {
-  if (!kEnableCheckCastEllision || !PerformOptimizations()) {
+  if (!kEnableCheckCastEllision) {
     return inst;
   }
   if (!driver_.IsSafeCast(&unit_, dex_pc)) {
@@ -254,7 +250,7 @@
                                              uint32_t dex_pc,
                                              Instruction::Code new_opcode,
                                              bool is_put) {
-  if (!kEnableQuickening || !PerformOptimizations()) {
+  if (!kEnableQuickening) {
     return;
   }
   uint32_t field_idx = inst->VRegC_22c();
@@ -279,7 +275,7 @@
 
 void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
                                        Instruction::Code new_opcode, bool is_range) {
-  if (!kEnableQuickening || !PerformOptimizations()) {
+  if (!kEnableQuickening) {
     return;
   }
   uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index 00c596d..87ddb39 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -34,8 +34,7 @@
 
 enum class DexToDexCompilationLevel {
   kDontDexToDexCompile,   // Only meaning wrt image time interpretation.
-  kRequired,              // Dex-to-dex compilation required for correctness.
-  kOptimize               // Perform required transformation and peep-hole optimizations.
+  kOptimize               // Perform peep-hole optimizations.
 };
 std::ostream& operator<<(std::ostream& os, const DexToDexCompilationLevel& rhs);
 
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 9950987..e823f67 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -532,16 +532,13 @@
   if (driver.GetCompilerOptions().GetDebuggable()) {
     // We are debuggable so definitions of classes might be changed. We don't want to do any
     // optimizations that could break that.
-    max_level = optimizer::DexToDexCompilationLevel::kRequired;
+    max_level = optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
   }
   if (klass->IsVerified()) {
     // Class is verified so we can enable DEX-to-DEX compilation for performance.
     return max_level;
-  } else if (klass->ShouldVerifyAtRuntime()) {
-    // Class verification has soft-failed. Anyway, ensure at least correctness.
-    return optimizer::DexToDexCompilationLevel::kRequired;
   } else {
-    // Class verification has failed: do not run DEX-to-DEX compilation.
+    // Class verification has failed: do not run DEX-to-DEX optimizations.
     return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
   }
 }
@@ -611,7 +608,7 @@
           dex_file,
           (verified_method != nullptr)
               ? dex_to_dex_compilation_level
-              : optimizer::DexToDexCompilationLevel::kRequired);
+              : optimizer::DexToDexCompilationLevel::kDontDexToDexCompile);
     }
   } else if ((access_flags & kAccNative) != 0) {
     // Are we extracting only and have support for generic JNI down calls?
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 34ad1c5..a0c0a2a 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -27,7 +27,6 @@
       small_method_threshold_(kDefaultSmallMethodThreshold),
       tiny_method_threshold_(kDefaultTinyMethodThreshold),
       num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold),
-      inline_depth_limit_(kUnsetInlineDepthLimit),
       inline_max_code_units_(kUnsetInlineMaxCodeUnits),
       no_inline_from_(nullptr),
       boot_image_(false),
@@ -62,7 +61,6 @@
                                  size_t small_method_threshold,
                                  size_t tiny_method_threshold,
                                  size_t num_dex_methods_threshold,
-                                 size_t inline_depth_limit,
                                  size_t inline_max_code_units,
                                  const std::vector<const DexFile*>* no_inline_from,
                                  double top_k_profile_threshold,
@@ -86,7 +84,6 @@
       small_method_threshold_(small_method_threshold),
       tiny_method_threshold_(tiny_method_threshold),
       num_dex_methods_threshold_(num_dex_methods_threshold),
-      inline_depth_limit_(inline_depth_limit),
       inline_max_code_units_(inline_max_code_units),
       no_inline_from_(no_inline_from),
       boot_image_(false),
@@ -130,10 +127,6 @@
   ParseUintOption(option, "--num-dex-methods", &num_dex_methods_threshold_, Usage);
 }
 
-void CompilerOptions::ParseInlineDepthLimit(const StringPiece& option, UsageFn Usage) {
-  ParseUintOption(option, "--inline-depth-limit", &inline_depth_limit_, Usage);
-}
-
 void CompilerOptions::ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage) {
   ParseUintOption(option, "--inline-max-code-units", &inline_max_code_units_, Usage);
 }
@@ -183,8 +176,6 @@
     ParseTinyMethodMax(option, Usage);
   } else if (option.starts_with("--num-dex-methods=")) {
     ParseNumDexMethods(option, Usage);
-  } else if (option.starts_with("--inline-depth-limit=")) {
-    ParseInlineDepthLimit(option, Usage);
   } else if (option.starts_with("--inline-max-code-units=")) {
     ParseInlineMaxCodeUnits(option, Usage);
   } else if (option == "--generate-debug-info" || option == "-g") {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 2e3e55f..2376fbf 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -46,15 +46,9 @@
   static constexpr double kDefaultTopKProfileThreshold = 90.0;
   static const bool kDefaultGenerateDebugInfo = false;
   static const bool kDefaultGenerateMiniDebugInfo = false;
-  static const size_t kDefaultInlineDepthLimit = 3;
   static const size_t kDefaultInlineMaxCodeUnits = 32;
-  static constexpr size_t kUnsetInlineDepthLimit = -1;
   static constexpr size_t kUnsetInlineMaxCodeUnits = -1;
 
-  // Default inlining settings when the space filter is used.
-  static constexpr size_t kSpaceFilterInlineDepthLimit = 3;
-  static constexpr size_t kSpaceFilterInlineMaxCodeUnits = 10;
-
   CompilerOptions();
   ~CompilerOptions();
 
@@ -64,7 +58,6 @@
                   size_t small_method_threshold,
                   size_t tiny_method_threshold,
                   size_t num_dex_methods_threshold,
-                  size_t inline_depth_limit,
                   size_t inline_max_code_units,
                   const std::vector<const DexFile*>* no_inline_from,
                   double top_k_profile_threshold,
@@ -155,13 +148,6 @@
     return num_dex_methods_threshold_;
   }
 
-  size_t GetInlineDepthLimit() const {
-    return inline_depth_limit_;
-  }
-  void SetInlineDepthLimit(size_t limit) {
-    inline_depth_limit_ = limit;
-  }
-
   size_t GetInlineMaxCodeUnits() const {
     return inline_max_code_units_;
   }
@@ -275,7 +261,6 @@
   void ParseDumpInitFailures(const StringPiece& option, UsageFn Usage);
   void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
   void ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage);
-  void ParseInlineDepthLimit(const StringPiece& option, UsageFn Usage);
   void ParseNumDexMethods(const StringPiece& option, UsageFn Usage);
   void ParseTinyMethodMax(const StringPiece& option, UsageFn Usage);
   void ParseSmallMethodMax(const StringPiece& option, UsageFn Usage);
@@ -289,7 +274,6 @@
   size_t small_method_threshold_;
   size_t tiny_method_threshold_;
   size_t num_dex_methods_threshold_;
-  size_t inline_depth_limit_;
   size_t inline_max_code_units_;
 
   // Dex files from which we should not inline code.
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 7ee494a..897d819 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -363,7 +363,6 @@
   }
   CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U);
   // Set inline filter values.
-  compiler_options_->SetInlineDepthLimit(CompilerOptions::kDefaultInlineDepthLimit);
   compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
   image_classes_.clear();
   if (!extra_dex.empty()) {
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 3ae7974..ad951bc 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -97,7 +97,6 @@
       CompilerOptions::kDefaultSmallMethodThreshold,
       CompilerOptions::kDefaultTinyMethodThreshold,
       CompilerOptions::kDefaultNumDexMethodsThreshold,
-      CompilerOptions::kDefaultInlineDepthLimit,
       CompilerOptions::kDefaultInlineMaxCodeUnits,
       /* no_inline_from */ nullptr,
       CompilerOptions::kDefaultTopKProfileThreshold,
@@ -177,10 +176,6 @@
     jit_logger_.reset(new JitLogger());
     jit_logger_->OpenLog();
   }
-
-  size_t inline_depth_limit = compiler_driver_->GetCompilerOptions().GetInlineDepthLimit();
-  DCHECK_LT(thread_count * inline_depth_limit, std::numeric_limits<uint16_t>::max())
-      << "ProfilingInfo's inline counter can potentially overflow";
 }
 
 JitCompiler::~JitCompiler() {
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 2ee4db9..476906a 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -528,7 +528,8 @@
         has_dom_based_dynamic_bce_(false),
         initial_block_size_(graph->GetBlocks().size()),
         side_effects_(side_effects),
-        induction_range_(induction_analysis) {}
+        induction_range_(induction_analysis),
+        next_(nullptr) {}
 
   void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
     DCHECK(!IsAddedBlock(block));
@@ -1618,8 +1619,8 @@
   void InsertDeoptInLoop(HLoopInformation* loop, HBasicBlock* block, HInstruction* condition) {
     HInstruction* suspend = loop->GetSuspendCheck();
     block->InsertInstructionBefore(condition, block->GetLastInstruction());
-    HDeoptimize* deoptimize =
-        new (GetGraph()->GetArena()) HDeoptimize(condition, suspend->GetDexPc());
+    HDeoptimize* deoptimize = new (GetGraph()->GetArena()) HDeoptimize(
+        GetGraph()->GetArena(), condition, HDeoptimize::Kind::kBCE, suspend->GetDexPc());
     block->InsertInstructionBefore(deoptimize, block->GetLastInstruction());
     if (suspend->HasEnvironment()) {
       deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment(
@@ -1631,8 +1632,8 @@
   void InsertDeoptInBlock(HBoundsCheck* bounds_check, HInstruction* condition) {
     HBasicBlock* block = bounds_check->GetBlock();
     block->InsertInstructionBefore(condition, bounds_check);
-    HDeoptimize* deoptimize =
-        new (GetGraph()->GetArena()) HDeoptimize(condition, bounds_check->GetDexPc());
+    HDeoptimize* deoptimize = new (GetGraph()->GetArena()) HDeoptimize(
+        GetGraph()->GetArena(), condition, HDeoptimize::Kind::kBCE, bounds_check->GetDexPc());
     block->InsertInstructionBefore(deoptimize, bounds_check);
     deoptimize->CopyEnvironmentFrom(bounds_check->GetEnvironment());
   }
diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc
index fe42301..048073e 100644
--- a/compiler/optimizing/cha_guard_optimization.cc
+++ b/compiler/optimizing/cha_guard_optimization.cc
@@ -36,7 +36,8 @@
       : HGraphVisitor(graph),
         block_has_cha_guard_(GetGraph()->GetBlocks().size(),
                              0,
-                             graph->GetArena()->Adapter(kArenaAllocCHA)) {
+                             graph->GetArena()->Adapter(kArenaAllocCHA)),
+        instruction_iterator_(nullptr) {
     number_of_guards_to_visit_ = GetGraph()->GetNumberOfCHAGuards();
     DCHECK_NE(number_of_guards_to_visit_, 0u);
     // Will recount number of guards during guard optimization.
@@ -201,8 +202,8 @@
     HInstruction* suspend = loop_info->GetSuspendCheck();
     // Need a new deoptimize instruction that copies the environment
     // of the suspend instruction for the loop.
-    HDeoptimize* deoptimize =
-        new (GetGraph()->GetArena()) HDeoptimize(compare, suspend->GetDexPc());
+    HDeoptimize* deoptimize = new (GetGraph()->GetArena()) HDeoptimize(
+        GetGraph()->GetArena(), compare, HDeoptimize::Kind::kInline, suspend->GetDexPc());
     pre_header->InsertInstructionBefore(deoptimize, pre_header->GetLastInstruction());
     deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment(
         suspend->GetEnvironment(), loop_info->GetHeader());
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index caea250..d7cc577 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1134,7 +1134,7 @@
            instruction_->IsArrayGet() ||
            instruction_->IsInstanceOf() ||
            instruction_->IsCheckCast() ||
-           (instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
+           (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
         << "Unexpected instruction in read barrier for heap reference slow path: "
         << instruction_->DebugName();
     // The read barrier instrumentation of object ArrayGet
@@ -1602,14 +1602,20 @@
   }
 }
 
-static Condition GenerateLongTestConstant(HCondition* condition,
-                                          bool invert,
-                                          CodeGeneratorARM* codegen) {
+static std::pair<Condition, Condition> GenerateLongTestConstant(HCondition* condition,
+                                                                bool invert,
+                                                                CodeGeneratorARM* codegen) {
   DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
 
   const LocationSummary* const locations = condition->GetLocations();
-  IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
-  Condition ret = EQ;
+  IfCondition cond = condition->GetCondition();
+  IfCondition opposite = condition->GetOppositeCondition();
+
+  if (invert) {
+    std::swap(cond, opposite);
+  }
+
+  std::pair<Condition, Condition> ret;
   const Location left = locations->InAt(0);
   const Location right = locations->InAt(1);
 
@@ -1629,22 +1635,26 @@
       __ CmpConstant(left_high, High32Bits(value));
       __ it(EQ);
       __ cmp(left_low, ShifterOperand(Low32Bits(value)), EQ);
-      ret = ARMUnsignedCondition(cond);
+      ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
       break;
     case kCondLE:
     case kCondGT:
       // Trivially true or false.
       if (value == std::numeric_limits<int64_t>::max()) {
         __ cmp(left_low, ShifterOperand(left_low));
-        ret = cond == kCondLE ? EQ : NE;
+        ret = cond == kCondLE ? std::make_pair(EQ, NE) : std::make_pair(NE, EQ);
         break;
       }
 
       if (cond == kCondLE) {
+        DCHECK_EQ(opposite, kCondGT);
         cond = kCondLT;
+        opposite = kCondGE;
       } else {
         DCHECK_EQ(cond, kCondGT);
+        DCHECK_EQ(opposite, kCondLE);
         cond = kCondGE;
+        opposite = kCondLT;
       }
 
       value++;
@@ -1653,7 +1663,7 @@
     case kCondLT:
       __ CmpConstant(left_low, Low32Bits(value));
       __ sbcs(IP, left_high, ShifterOperand(High32Bits(value)));
-      ret = ARMCondition(cond);
+      ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
       break;
     default:
       LOG(FATAL) << "Unreachable";
@@ -1663,14 +1673,20 @@
   return ret;
 }
 
-static Condition GenerateLongTest(HCondition* condition,
-                                  bool invert,
-                                  CodeGeneratorARM* codegen) {
+static std::pair<Condition, Condition> GenerateLongTest(HCondition* condition,
+                                                        bool invert,
+                                                        CodeGeneratorARM* codegen) {
   DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
 
   const LocationSummary* const locations = condition->GetLocations();
-  IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
-  Condition ret = EQ;
+  IfCondition cond = condition->GetCondition();
+  IfCondition opposite = condition->GetOppositeCondition();
+
+  if (invert) {
+    std::swap(cond, opposite);
+  }
+
+  std::pair<Condition, Condition> ret;
   Location left = locations->InAt(0);
   Location right = locations->InAt(1);
 
@@ -1689,15 +1705,19 @@
       __ cmp(left.AsRegisterPairLow<Register>(),
              ShifterOperand(right.AsRegisterPairLow<Register>()),
              EQ);
-      ret = ARMUnsignedCondition(cond);
+      ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
       break;
     case kCondLE:
     case kCondGT:
       if (cond == kCondLE) {
+        DCHECK_EQ(opposite, kCondGT);
         cond = kCondGE;
+        opposite = kCondLT;
       } else {
         DCHECK_EQ(cond, kCondGT);
+        DCHECK_EQ(opposite, kCondLE);
         cond = kCondLT;
+        opposite = kCondGE;
       }
 
       std::swap(left, right);
@@ -1709,7 +1729,7 @@
       __ sbcs(IP,
               left.AsRegisterPairHigh<Register>(),
               ShifterOperand(right.AsRegisterPairHigh<Register>()));
-      ret = ARMCondition(cond);
+      ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
       break;
     default:
       LOG(FATAL) << "Unreachable";
@@ -1719,90 +1739,83 @@
   return ret;
 }
 
-static Condition GenerateTest(HInstruction* instruction,
-                              Location loc,
-                              bool invert,
-                              CodeGeneratorARM* codegen) {
-  DCHECK(!instruction->IsConstant());
+static std::pair<Condition, Condition> GenerateTest(HCondition* condition,
+                                                    bool invert,
+                                                    CodeGeneratorARM* codegen) {
+  const LocationSummary* const locations = condition->GetLocations();
+  const Primitive::Type type = condition->GetLeft()->GetType();
+  IfCondition cond = condition->GetCondition();
+  IfCondition opposite = condition->GetOppositeCondition();
+  std::pair<Condition, Condition> ret;
+  const Location right = locations->InAt(1);
 
-  Condition ret = invert ? EQ : NE;
+  if (invert) {
+    std::swap(cond, opposite);
+  }
 
-  if (IsBooleanValueOrMaterializedCondition(instruction)) {
-    __ CmpConstant(loc.AsRegister<Register>(), 0);
+  if (type == Primitive::kPrimLong) {
+    ret = locations->InAt(1).IsConstant()
+        ? GenerateLongTestConstant(condition, invert, codegen)
+        : GenerateLongTest(condition, invert, codegen);
+  } else if (Primitive::IsFloatingPointType(type)) {
+    GenerateVcmp(condition, codegen);
+    __ vmstat();
+    ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
+                         ARMFPCondition(opposite, condition->IsGtBias()));
   } else {
-    HCondition* const condition = instruction->AsCondition();
-    const LocationSummary* const locations = condition->GetLocations();
-    const Primitive::Type type = condition->GetLeft()->GetType();
-    const IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
-    const Location right = locations->InAt(1);
+    DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
 
-    if (type == Primitive::kPrimLong) {
-      ret = condition->GetLocations()->InAt(1).IsConstant()
-          ? GenerateLongTestConstant(condition, invert, codegen)
-          : GenerateLongTest(condition, invert, codegen);
-    } else if (Primitive::IsFloatingPointType(type)) {
-      GenerateVcmp(condition, codegen);
-      __ vmstat();
-      ret = ARMFPCondition(cond, condition->IsGtBias());
+    const Register left = locations->InAt(0).AsRegister<Register>();
+
+    if (right.IsRegister()) {
+      __ cmp(left, ShifterOperand(right.AsRegister<Register>()));
     } else {
-      DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
-
-      const Register left = locations->InAt(0).AsRegister<Register>();
-
-      if (right.IsRegister()) {
-        __ cmp(left, ShifterOperand(right.AsRegister<Register>()));
-      } else {
-        DCHECK(right.IsConstant());
-        __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
-      }
-
-      ret = ARMCondition(cond);
+      DCHECK(right.IsConstant());
+      __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
     }
+
+    ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
   }
 
   return ret;
 }
 
-static bool CanGenerateTest(HInstruction* condition, ArmAssembler* assembler) {
-  if (!IsBooleanValueOrMaterializedCondition(condition)) {
-    const HCondition* const cond = condition->AsCondition();
+static bool CanGenerateTest(HCondition* condition, ArmAssembler* assembler) {
+  if (condition->GetLeft()->GetType() == Primitive::kPrimLong) {
+    const LocationSummary* const locations = condition->GetLocations();
+    const IfCondition c = condition->GetCondition();
 
-    if (cond->GetLeft()->GetType() == Primitive::kPrimLong) {
-      const LocationSummary* const locations = cond->GetLocations();
-      const IfCondition c = cond->GetCondition();
+    if (locations->InAt(1).IsConstant()) {
+      const int64_t value = locations->InAt(1).GetConstant()->AsLongConstant()->GetValue();
+      ShifterOperand so;
 
-      if (locations->InAt(1).IsConstant()) {
-        const int64_t value = locations->InAt(1).GetConstant()->AsLongConstant()->GetValue();
-        ShifterOperand so;
-
-        if (c < kCondLT || c > kCondGE) {
-          // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
-          // we check that the least significant half of the first input to be compared
-          // is in a low register (the other half is read outside an IT block), and
-          // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP
-          // encoding can be used.
-          if (!ArmAssembler::IsLowRegister(locations->InAt(0).AsRegisterPairLow<Register>()) ||
-              !IsUint<8>(Low32Bits(value))) {
-            return false;
-          }
-        } else if (c == kCondLE || c == kCondGT) {
-          if (value < std::numeric_limits<int64_t>::max() &&
-              !assembler->ShifterOperandCanHold(kNoRegister,
-                                                kNoRegister,
-                                                SBC,
-                                                High32Bits(value + 1),
-                                                kCcSet,
-                                                &so)) {
-            return false;
-          }
-        } else if (!assembler->ShifterOperandCanHold(kNoRegister,
-                                                     kNoRegister,
-                                                     SBC,
-                                                     High32Bits(value),
-                                                     kCcSet,
-                                                     &so)) {
+      if (c < kCondLT || c > kCondGE) {
+        // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+        // we check that the least significant half of the first input to be compared
+        // is in a low register (the other half is read outside an IT block), and
+        // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP
+        // encoding can be used.
+        if (!ArmAssembler::IsLowRegister(locations->InAt(0).AsRegisterPairLow<Register>()) ||
+            !IsUint<8>(Low32Bits(value))) {
           return false;
         }
+      } else if (c == kCondLE || c == kCondGT) {
+        if (value < std::numeric_limits<int64_t>::max() &&
+            !assembler->ShifterOperandCanHold(kNoRegister,
+                                              kNoRegister,
+                                              SBC,
+                                              High32Bits(value + 1),
+                                              kCcSet,
+                                              &so)) {
+          return false;
+        }
+      } else if (!assembler->ShifterOperandCanHold(kNoRegister,
+                                                   kNoRegister,
+                                                   SBC,
+                                                   High32Bits(value),
+                                                   kCcSet,
+                                                   &so)) {
+        return false;
       }
     }
   }
@@ -2415,13 +2428,6 @@
 void InstructionCodeGeneratorARM::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
 }
 
-void InstructionCodeGeneratorARM::GenerateFPJumps(HCondition* cond,
-                                                  Label* true_label,
-                                                  Label* false_label ATTRIBUTE_UNUSED) {
-  __ vmstat();  // transfer FP status register to ARM APSR.
-  __ b(true_label, ARMFPCondition(cond->GetCondition(), cond->IsGtBias()));
-}
-
 void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond,
                                                                Label* true_label,
                                                                Label* false_label) {
@@ -2438,7 +2444,6 @@
 
   // Set the conditions for the test, remembering that == needs to be
   // decided using the low words.
-  // TODO: consider avoiding jumps with temporary and CMP low+SBC high
   switch (if_cond) {
     case kCondEQ:
     case kCondNE:
@@ -2509,25 +2514,38 @@
 void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HCondition* condition,
                                                                Label* true_target_in,
                                                                Label* false_target_in) {
+  if (CanGenerateTest(condition, codegen_->GetAssembler())) {
+    Label* non_fallthrough_target;
+    bool invert;
+
+    if (true_target_in == nullptr) {
+      DCHECK(false_target_in != nullptr);
+      non_fallthrough_target = false_target_in;
+      invert = true;
+    } else {
+      non_fallthrough_target = true_target_in;
+      invert = false;
+    }
+
+    const auto cond = GenerateTest(condition, invert, codegen_);
+
+    __ b(non_fallthrough_target, cond.first);
+
+    if (false_target_in != nullptr && false_target_in != non_fallthrough_target) {
+      __ b(false_target_in);
+    }
+
+    return;
+  }
+
   // Generated branching requires both targets to be explicit. If either of the
   // targets is nullptr (fallthrough) use and bind `fallthrough_target` instead.
   Label fallthrough_target;
   Label* true_target = true_target_in == nullptr ? &fallthrough_target : true_target_in;
   Label* false_target = false_target_in == nullptr ? &fallthrough_target : false_target_in;
 
-  Primitive::Type type = condition->InputAt(0)->GetType();
-  switch (type) {
-    case Primitive::kPrimLong:
-      GenerateLongComparesAndJumps(condition, true_target, false_target);
-      break;
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      GenerateVcmp(condition, codegen_);
-      GenerateFPJumps(condition, true_target, false_target);
-      break;
-    default:
-      LOG(FATAL) << "Unexpected compare type " << type;
-  }
+  DCHECK_EQ(condition->InputAt(0)->GetType(), Primitive::kPrimLong);
+  GenerateLongComparesAndJumps(condition, true_target, false_target);
 
   if (false_target != &fallthrough_target) {
     __ b(false_target);
@@ -2729,7 +2747,8 @@
   }
 
   if (!Primitive::IsFloatingPointType(type) &&
-      CanGenerateTest(condition, codegen_->GetAssembler())) {
+      (IsBooleanValueOrMaterializedCondition(condition) ||
+       CanGenerateTest(condition->AsCondition(), codegen_->GetAssembler()))) {
     bool invert = false;
 
     if (out.Equals(second)) {
@@ -2753,7 +2772,14 @@
         codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
       }
 
-      const Condition cond = GenerateTest(condition, locations->InAt(2), invert, codegen_);
+      std::pair<Condition, Condition> cond;
+
+      if (IsBooleanValueOrMaterializedCondition(condition)) {
+        __ CmpConstant(locations->InAt(2).AsRegister<Register>(), 0);
+        cond = invert ? std::make_pair(EQ, NE) : std::make_pair(NE, EQ);
+      } else {
+        cond = GenerateTest(condition->AsCondition(), invert, codegen_);
+      }
 
       if (out.IsRegister()) {
         ShifterOperand operand;
@@ -2765,8 +2791,8 @@
           operand = ShifterOperand(src.AsRegister<Register>());
         }
 
-        __ it(cond);
-        __ mov(out.AsRegister<Register>(), operand, cond);
+        __ it(cond.first);
+        __ mov(out.AsRegister<Register>(), operand, cond.first);
       } else {
         DCHECK(out.IsRegisterPair());
 
@@ -2784,10 +2810,10 @@
           operand_low = ShifterOperand(src.AsRegisterPairLow<Register>());
         }
 
-        __ it(cond);
-        __ mov(out.AsRegisterPairLow<Register>(), operand_low, cond);
-        __ it(cond);
-        __ mov(out.AsRegisterPairHigh<Register>(), operand_high, cond);
+        __ it(cond.first);
+        __ mov(out.AsRegisterPairLow<Register>(), operand_low, cond.first);
+        __ it(cond.first);
+        __ mov(out.AsRegisterPairHigh<Register>(), operand_high, cond.first);
       }
 
       return;
@@ -2840,7 +2866,7 @@
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
       if (!cond->IsEmittedAtUseSite()) {
-        locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       }
       break;
 
@@ -2867,51 +2893,44 @@
     return;
   }
 
-  LocationSummary* locations = cond->GetLocations();
-  Location left = locations->InAt(0);
-  Location right = locations->InAt(1);
-  Register out = locations->Out().AsRegister<Register>();
-  Label true_label, false_label;
+  const Register out = cond->GetLocations()->Out().AsRegister<Register>();
 
-  switch (cond->InputAt(0)->GetType()) {
-    default: {
-      // Integer case.
-      if (right.IsRegister()) {
-        __ cmp(left.AsRegister<Register>(), ShifterOperand(right.AsRegister<Register>()));
-      } else {
-        DCHECK(right.IsConstant());
-        __ CmpConstant(left.AsRegister<Register>(),
-                       CodeGenerator::GetInt32ValueOf(right.GetConstant()));
-      }
-      __ it(ARMCondition(cond->GetCondition()), kItElse);
-      __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(1),
-             ARMCondition(cond->GetCondition()));
-      __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(0),
-             ARMCondition(cond->GetOppositeCondition()));
-      return;
-    }
-    case Primitive::kPrimLong:
-      GenerateLongComparesAndJumps(cond, &true_label, &false_label);
-      break;
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      GenerateVcmp(cond, codegen_);
-      GenerateFPJumps(cond, &true_label, &false_label);
-      break;
+  if (ArmAssembler::IsLowRegister(out) && CanGenerateTest(cond, codegen_->GetAssembler())) {
+    const auto condition = GenerateTest(cond, false, codegen_);
+
+    __ it(condition.first);
+    __ mov(out, ShifterOperand(1), condition.first);
+    __ it(condition.second);
+    __ mov(out, ShifterOperand(0), condition.second);
+    return;
   }
 
   // Convert the jumps into the result.
   Label done_label;
-  Label* final_label = codegen_->GetFinalLabel(cond, &done_label);
+  Label* const final_label = codegen_->GetFinalLabel(cond, &done_label);
 
-  // False case: result = 0.
-  __ Bind(&false_label);
-  __ LoadImmediate(out, 0);
-  __ b(final_label);
+  if (cond->InputAt(0)->GetType() == Primitive::kPrimLong) {
+    Label true_label, false_label;
 
-  // True case: result = 1.
-  __ Bind(&true_label);
-  __ LoadImmediate(out, 1);
+    GenerateLongComparesAndJumps(cond, &true_label, &false_label);
+
+    // False case: result = 0.
+    __ Bind(&false_label);
+    __ LoadImmediate(out, 0);
+    __ b(final_label);
+
+    // True case: result = 1.
+    __ Bind(&true_label);
+    __ LoadImmediate(out, 1);
+  } else {
+    DCHECK(CanGenerateTest(cond, codegen_->GetAssembler()));
+
+    const auto condition = GenerateTest(cond, false, codegen_);
+
+    __ mov(out, ShifterOperand(0), AL, kCcKeep);
+    __ b(final_label, condition.second);
+    __ LoadImmediate(out, 1);
+  }
 
   if (done_label.IsLinked()) {
     __ Bind(&done_label);
@@ -7039,14 +7058,16 @@
   uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
   uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
   uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
-  Label done, zero;
-  Label* final_label = codegen_->GetFinalLabel(instruction, &done);
+  Label done;
+  Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
   SlowPathCodeARM* slow_path = nullptr;
 
   // Return 0 if `obj` is null.
   // avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
-    __ CompareAndBranchIfZero(obj, &zero);
+    DCHECK_NE(out, obj);
+    __ LoadImmediate(out, 0);
+    __ CompareAndBranchIfZero(obj, final_label);
   }
 
   switch (type_check_kind) {
@@ -7058,11 +7079,23 @@
                                         class_offset,
                                         maybe_temp_loc,
                                         kCompilerReadBarrierOption);
-      __ cmp(out, ShifterOperand(cls));
       // Classes must be equal for the instanceof to succeed.
-      __ b(&zero, NE);
-      __ LoadImmediate(out, 1);
-      __ b(final_label);
+      __ cmp(out, ShifterOperand(cls));
+      // We speculatively set the result to false without changing the condition
+      // flags, which allows us to avoid some branching later.
+      __ mov(out, ShifterOperand(0), AL, kCcKeep);
+
+      // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+      // we check that the output is in a low register, so that a 16-bit MOV
+      // encoding can be used.
+      if (ArmAssembler::IsLowRegister(out)) {
+        __ it(EQ);
+        __ mov(out, ShifterOperand(1), EQ);
+      } else {
+        __ b(final_label, NE);
+        __ LoadImmediate(out, 1);
+      }
+
       break;
     }
 
@@ -7084,14 +7117,11 @@
                                        super_offset,
                                        maybe_temp_loc,
                                        kCompilerReadBarrierOption);
-      // If `out` is null, we use it for the result, and jump to `done`.
+      // If `out` is null, we use it for the result, and jump to the final label.
       __ CompareAndBranchIfZero(out, final_label);
       __ cmp(out, ShifterOperand(cls));
       __ b(&loop, NE);
       __ LoadImmediate(out, 1);
-      if (zero.IsLinked()) {
-        __ b(final_label);
-      }
       break;
     }
 
@@ -7114,14 +7144,32 @@
                                        super_offset,
                                        maybe_temp_loc,
                                        kCompilerReadBarrierOption);
-      __ CompareAndBranchIfNonZero(out, &loop);
-      // If `out` is null, we use it for the result, and jump to `done`.
-      __ b(final_label);
-      __ Bind(&success);
-      __ LoadImmediate(out, 1);
-      if (zero.IsLinked()) {
+      // This is essentially a null check, but it sets the condition flags to the
+      // proper value for the code that follows the loop, i.e. not `EQ`.
+      __ cmp(out, ShifterOperand(1));
+      __ b(&loop, HS);
+
+      // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+      // we check that the output is in a low register, so that a 16-bit MOV
+      // encoding can be used.
+      if (ArmAssembler::IsLowRegister(out)) {
+        // If `out` is null, we use it for the result, and the condition flags
+        // have already been set to `NE`, so the IT block that comes afterwards
+        // (and which handles the successful case) turns into a NOP (instead of
+        // overwriting `out`).
+        __ Bind(&success);
+        // There is only one branch to the `success` label (which is bound to this
+        // IT block), and it has the same condition, `EQ`, so in that case the MOV
+        // is executed.
+        __ it(EQ);
+        __ mov(out, ShifterOperand(1), EQ);
+      } else {
+        // If `out` is null, we use it for the result, and jump to the final label.
         __ b(final_label);
+        __ Bind(&success);
+        __ LoadImmediate(out, 1);
       }
+
       break;
     }
 
@@ -7144,14 +7192,28 @@
                                        component_offset,
                                        maybe_temp_loc,
                                        kCompilerReadBarrierOption);
-      // If `out` is null, we use it for the result, and jump to `done`.
+      // If `out` is null, we use it for the result, and jump to the final label.
       __ CompareAndBranchIfZero(out, final_label);
       __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-      __ CompareAndBranchIfNonZero(out, &zero);
-      __ Bind(&exact_check);
-      __ LoadImmediate(out, 1);
-      __ b(final_label);
+      __ cmp(out, ShifterOperand(0));
+      // We speculatively set the result to false without changing the condition
+      // flags, which allows us to avoid some branching later.
+      __ mov(out, ShifterOperand(0), AL, kCcKeep);
+
+      // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+      // we check that the output is in a low register, so that a 16-bit MOV
+      // encoding can be used.
+      if (ArmAssembler::IsLowRegister(out)) {
+        __ Bind(&exact_check);
+        __ it(EQ);
+        __ mov(out, ShifterOperand(1), EQ);
+      } else {
+        __ b(final_label, NE);
+        __ Bind(&exact_check);
+        __ LoadImmediate(out, 1);
+      }
+
       break;
     }
 
@@ -7171,9 +7233,6 @@
       codegen_->AddSlowPath(slow_path);
       __ b(slow_path->GetEntryLabel(), NE);
       __ LoadImmediate(out, 1);
-      if (zero.IsLinked()) {
-        __ b(final_label);
-      }
       break;
     }
 
@@ -7202,18 +7261,10 @@
                                                                     /* is_fatal */ false);
       codegen_->AddSlowPath(slow_path);
       __ b(slow_path->GetEntryLabel());
-      if (zero.IsLinked()) {
-        __ b(final_label);
-      }
       break;
     }
   }
 
-  if (zero.IsLinked()) {
-    __ Bind(&zero);
-    __ LoadImmediate(out, 0);
-  }
-
   if (done.IsLinked()) {
     __ Bind(&done);
   }
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 59a7f7c..86f2f21 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -299,7 +299,6 @@
   void GenerateCompareTestAndBranch(HCondition* condition,
                                     Label* true_target,
                                     Label* false_target);
-  void GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label);
   void GenerateLongComparesAndJumps(HCondition* cond, Label* true_label, Label* false_label);
   void DivRemOneOrMinusOne(HBinaryOperation* instruction);
   void DivRemByPowerOfTwo(HBinaryOperation* instruction);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 7257fc5..d463830 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1150,7 +1150,7 @@
            instruction_->IsArrayGet() ||
            instruction_->IsInstanceOf() ||
            instruction_->IsCheckCast() ||
-           (instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
+           (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
         << "Unexpected instruction in read barrier for heap reference slow path: "
         << instruction_->DebugName();
     // The read barrier instrumentation of object ArrayGet
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 2d2d810..cce412b 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -1175,7 +1175,7 @@
            instruction_->IsArrayGet() ||
            instruction_->IsInstanceOf() ||
            instruction_->IsCheckCast() ||
-           (instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
+           (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
         << "Unexpected instruction in read barrier for heap reference slow path: "
         << instruction_->DebugName();
     // The read barrier instrumentation of object ArrayGet
@@ -1687,14 +1687,21 @@
   }
 }
 
-static vixl32::Condition GenerateLongTestConstant(HCondition* condition,
-                                                  bool invert,
-                                                  CodeGeneratorARMVIXL* codegen) {
+static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTestConstant(
+    HCondition* condition,
+    bool invert,
+    CodeGeneratorARMVIXL* codegen) {
   DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
 
   const LocationSummary* const locations = condition->GetLocations();
-  IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
-  vixl32::Condition ret = eq;
+  IfCondition cond = condition->GetCondition();
+  IfCondition opposite = condition->GetOppositeCondition();
+
+  if (invert) {
+    std::swap(cond, opposite);
+  }
+
+  std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
   const Location left = locations->InAt(0);
   const Location right = locations->InAt(1);
 
@@ -1713,13 +1720,14 @@
     case kCondAE: {
       __ Cmp(left_high, High32Bits(value));
 
+      // We use the scope because of the IT block that follows.
       ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
                                2 * vixl32::k16BitT32InstructionSizeInBytes,
                                CodeBufferCheckScope::kExactSize);
 
       __ it(eq);
       __ cmp(eq, left_low, Low32Bits(value));
-      ret = ARMUnsignedCondition(cond);
+      ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
       break;
     }
     case kCondLE:
@@ -1727,15 +1735,19 @@
       // Trivially true or false.
       if (value == std::numeric_limits<int64_t>::max()) {
         __ Cmp(left_low, left_low);
-        ret = cond == kCondLE ? eq : ne;
+        ret = cond == kCondLE ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
         break;
       }
 
       if (cond == kCondLE) {
+        DCHECK_EQ(opposite, kCondGT);
         cond = kCondLT;
+        opposite = kCondGE;
       } else {
         DCHECK_EQ(cond, kCondGT);
+        DCHECK_EQ(opposite, kCondLE);
         cond = kCondGE;
+        opposite = kCondLT;
       }
 
       value++;
@@ -1746,7 +1758,7 @@
 
       __ Cmp(left_low, Low32Bits(value));
       __ Sbcs(temps.Acquire(), left_high, High32Bits(value));
-      ret = ARMCondition(cond);
+      ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
       break;
     }
     default:
@@ -1757,14 +1769,21 @@
   return ret;
 }
 
-static vixl32::Condition GenerateLongTest(HCondition* condition,
-                                          bool invert,
-                                          CodeGeneratorARMVIXL* codegen) {
+static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTest(
+    HCondition* condition,
+    bool invert,
+    CodeGeneratorARMVIXL* codegen) {
   DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
 
   const LocationSummary* const locations = condition->GetLocations();
-  IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
-  vixl32::Condition ret = eq;
+  IfCondition cond = condition->GetCondition();
+  IfCondition opposite = condition->GetOppositeCondition();
+
+  if (invert) {
+    std::swap(cond, opposite);
+  }
+
+  std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
   Location left = locations->InAt(0);
   Location right = locations->InAt(1);
 
@@ -1779,22 +1798,27 @@
     case kCondAE: {
       __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));
 
+      // We use the scope because of the IT block that follows.
       ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
                                2 * vixl32::k16BitT32InstructionSizeInBytes,
                                CodeBufferCheckScope::kExactSize);
 
       __ it(eq);
       __ cmp(eq, LowRegisterFrom(left), LowRegisterFrom(right));
-      ret = ARMUnsignedCondition(cond);
+      ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
       break;
     }
     case kCondLE:
     case kCondGT:
       if (cond == kCondLE) {
+        DCHECK_EQ(opposite, kCondGT);
         cond = kCondGE;
+        opposite = kCondLT;
       } else {
         DCHECK_EQ(cond, kCondGT);
+        DCHECK_EQ(opposite, kCondLE);
         cond = kCondLT;
+        opposite = kCondGE;
       }
 
       std::swap(left, right);
@@ -1805,7 +1829,7 @@
 
       __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));
       __ Sbcs(temps.Acquire(), HighRegisterFrom(left), HighRegisterFrom(right));
-      ret = ARMCondition(cond);
+      ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
       break;
     }
     default:
@@ -1816,69 +1840,62 @@
   return ret;
 }
 
-static vixl32::Condition GenerateTest(HInstruction* instruction,
-                                      Location loc,
-                                      bool invert,
-                                      CodeGeneratorARMVIXL* codegen) {
-  DCHECK(!instruction->IsConstant());
+static std::pair<vixl32::Condition, vixl32::Condition> GenerateTest(HCondition* condition,
+                                                                    bool invert,
+                                                                    CodeGeneratorARMVIXL* codegen) {
+  const Primitive::Type type = condition->GetLeft()->GetType();
+  IfCondition cond = condition->GetCondition();
+  IfCondition opposite = condition->GetOppositeCondition();
+  std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
 
-  vixl32::Condition ret = invert ? eq : ne;
+  if (invert) {
+    std::swap(cond, opposite);
+  }
 
-  if (IsBooleanValueOrMaterializedCondition(instruction)) {
-    __ Cmp(RegisterFrom(loc), 0);
+  if (type == Primitive::kPrimLong) {
+    ret = condition->GetLocations()->InAt(1).IsConstant()
+        ? GenerateLongTestConstant(condition, invert, codegen)
+        : GenerateLongTest(condition, invert, codegen);
+  } else if (Primitive::IsFloatingPointType(type)) {
+    GenerateVcmp(condition, codegen);
+    __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
+    ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
+                         ARMFPCondition(opposite, condition->IsGtBias()));
   } else {
-    HCondition* const condition = instruction->AsCondition();
-    const Primitive::Type type = condition->GetLeft()->GetType();
-    const IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
-
-    if (type == Primitive::kPrimLong) {
-      ret = condition->GetLocations()->InAt(1).IsConstant()
-          ? GenerateLongTestConstant(condition, invert, codegen)
-          : GenerateLongTest(condition, invert, codegen);
-    } else if (Primitive::IsFloatingPointType(type)) {
-      GenerateVcmp(condition, codegen);
-      __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
-      ret = ARMFPCondition(cond, condition->IsGtBias());
-    } else {
-      DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
-      __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1));
-      ret = ARMCondition(cond);
-    }
+    DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
+    __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1));
+    ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
   }
 
   return ret;
 }
 
-static bool CanGenerateTest(HInstruction* condition, ArmVIXLAssembler* assembler) {
-  if (!IsBooleanValueOrMaterializedCondition(condition)) {
-    const HCondition* const cond = condition->AsCondition();
+static bool CanGenerateTest(HCondition* condition, ArmVIXLAssembler* assembler) {
+  if (condition->GetLeft()->GetType() == Primitive::kPrimLong) {
+    const LocationSummary* const locations = condition->GetLocations();
+    const IfCondition c = condition->GetCondition();
 
-    if (cond->GetLeft()->GetType() == Primitive::kPrimLong) {
-      const LocationSummary* const locations = cond->GetLocations();
-      const IfCondition c = cond->GetCondition();
+    if (locations->InAt(1).IsConstant()) {
+      const int64_t value = Int64ConstantFrom(locations->InAt(1));
 
-      if (locations->InAt(1).IsConstant()) {
-        const int64_t value = Int64ConstantFrom(locations->InAt(1));
-
-        if (c < kCondLT || c > kCondGE) {
-          // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
-          // we check that the least significant half of the first input to be compared
-          // is in a low register (the other half is read outside an IT block), and
-          // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP
-          // encoding can be used.
-          if (!LowRegisterFrom(locations->InAt(0)).IsLow() || !IsUint<8>(Low32Bits(value))) {
-            return false;
-          }
-        // TODO(VIXL): The rest of the checks are there to keep the backend in sync with
-        // the previous one, but are not strictly necessary.
-        } else if (c == kCondLE || c == kCondGT) {
-          if (value < std::numeric_limits<int64_t>::max() &&
-              !assembler->ShifterOperandCanHold(SBC, High32Bits(value + 1), kCcSet)) {
-            return false;
-          }
-        } else if (!assembler->ShifterOperandCanHold(SBC, High32Bits(value), kCcSet)) {
+      if (c < kCondLT || c > kCondGE) {
+        // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+        // we check that the least significant half of the first input to be compared
+        // is in a low register (the other half is read outside an IT block), and
+        // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP
+        // encoding can be used.
+        if (!LowRegisterFrom(locations->InAt(0)).IsLow() || !IsUint<8>(Low32Bits(value))) {
           return false;
         }
+      // TODO(VIXL): The rest of the checks are there to keep the backend in sync with
+      // the previous one, but are not strictly necessary.
+      } else if (c == kCondLE || c == kCondGT) {
+        if (value < std::numeric_limits<int64_t>::max() &&
+            !assembler->ShifterOperandCanHold(SBC, High32Bits(value + 1), kCcSet)) {
+          return false;
+        }
+      } else if (!assembler->ShifterOperandCanHold(SBC, High32Bits(value), kCcSet)) {
+        return false;
       }
     }
   }
@@ -2445,14 +2462,6 @@
 void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
 }
 
-void InstructionCodeGeneratorARMVIXL::GenerateFPJumps(HCondition* cond,
-                                                      vixl32::Label* true_label,
-                                                      vixl32::Label* false_label ATTRIBUTE_UNUSED) {
-  // To branch on the result of the FP compare we transfer FPSCR to APSR (encoded as PC in VMRS).
-  __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
-  __ B(ARMFPCondition(cond->GetCondition(), cond->IsGtBias()), true_label);
-}
-
 void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* cond,
                                                                    vixl32::Label* true_label,
                                                                    vixl32::Label* false_label) {
@@ -2469,7 +2478,6 @@
 
   // Set the conditions for the test, remembering that == needs to be
   // decided using the low words.
-  // TODO: consider avoiding jumps with temporary and CMP low+SBC high
   switch (if_cond) {
     case kCondEQ:
     case kCondNE:
@@ -2540,31 +2548,44 @@
 void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
                                                                    vixl32::Label* true_target_in,
                                                                    vixl32::Label* false_target_in) {
+  if (CanGenerateTest(condition, codegen_->GetAssembler())) {
+    vixl32::Label* non_fallthrough_target;
+    bool invert;
+
+    if (true_target_in == nullptr) {
+      DCHECK(false_target_in != nullptr);
+      non_fallthrough_target = false_target_in;
+      invert = true;
+    } else {
+      non_fallthrough_target = true_target_in;
+      invert = false;
+    }
+
+    const auto cond = GenerateTest(condition, invert, codegen_);
+
+    __ B(cond.first, non_fallthrough_target);
+
+    if (false_target_in != nullptr && false_target_in != non_fallthrough_target) {
+      __ B(false_target_in);
+    }
+
+    return;
+  }
+
   // Generated branching requires both targets to be explicit. If either of the
   // targets is nullptr (fallthrough) use and bind `fallthrough` instead.
   vixl32::Label fallthrough;
   vixl32::Label* true_target = (true_target_in == nullptr) ? &fallthrough : true_target_in;
   vixl32::Label* false_target = (false_target_in == nullptr) ? &fallthrough : false_target_in;
 
-  Primitive::Type type = condition->InputAt(0)->GetType();
-  switch (type) {
-    case Primitive::kPrimLong:
-      GenerateLongComparesAndJumps(condition, true_target, false_target);
-      break;
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      GenerateVcmp(condition, codegen_);
-      GenerateFPJumps(condition, true_target, false_target);
-      break;
-    default:
-      LOG(FATAL) << "Unexpected compare type " << type;
-  }
+  DCHECK_EQ(condition->InputAt(0)->GetType(), Primitive::kPrimLong);
+  GenerateLongComparesAndJumps(condition, true_target, false_target);
 
   if (false_target != &fallthrough) {
     __ B(false_target);
   }
 
-  if (true_target_in == nullptr || false_target_in == nullptr) {
+  if (fallthrough.IsReferenced()) {
     __ Bind(&fallthrough);
   }
 }
@@ -2759,7 +2780,8 @@
   }
 
   if (!Primitive::IsFloatingPointType(type) &&
-      CanGenerateTest(condition, codegen_->GetAssembler())) {
+      (IsBooleanValueOrMaterializedCondition(condition) ||
+       CanGenerateTest(condition->AsCondition(), codegen_->GetAssembler()))) {
     bool invert = false;
 
     if (out.Equals(second)) {
@@ -2783,15 +2805,24 @@
         codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
       }
 
-      const vixl32::Condition cond = GenerateTest(condition, locations->InAt(2), invert, codegen_);
+      std::pair<vixl32::Condition, vixl32::Condition> cond(eq, ne);
+
+      if (IsBooleanValueOrMaterializedCondition(condition)) {
+        __ Cmp(InputRegisterAt(select, 2), 0);
+        cond = invert ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
+      } else {
+        cond = GenerateTest(condition->AsCondition(), invert, codegen_);
+      }
+
       const size_t instr_count = out.IsRegisterPair() ? 4 : 2;
+      // We use the scope because of the IT block that follows.
       ExactAssemblyScope guard(GetVIXLAssembler(),
                                instr_count * vixl32::k16BitT32InstructionSizeInBytes,
                                CodeBufferCheckScope::kExactSize);
 
       if (out.IsRegister()) {
-        __ it(cond);
-        __ mov(cond, RegisterFrom(out), OperandFrom(src, type));
+        __ it(cond.first);
+        __ mov(cond.first, RegisterFrom(out), OperandFrom(src, type));
       } else {
         DCHECK(out.IsRegisterPair());
 
@@ -2809,10 +2840,10 @@
           operand_low = LowRegisterFrom(src);
         }
 
-        __ it(cond);
-        __ mov(cond, LowRegisterFrom(out), operand_low);
-        __ it(cond);
-        __ mov(cond, HighRegisterFrom(out), operand_high);
+        __ it(cond.first);
+        __ mov(cond.first, LowRegisterFrom(out), operand_low);
+        __ it(cond.first);
+        __ mov(cond.first, HighRegisterFrom(out), operand_high);
       }
 
       return;
@@ -2865,7 +2896,7 @@
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
       if (!cond->IsEmittedAtUseSite()) {
-        locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       }
       break;
 
@@ -2892,50 +2923,48 @@
     return;
   }
 
-  Location right = cond->GetLocations()->InAt(1);
-  vixl32::Register out = OutputRegister(cond);
-  vixl32::Label true_label, false_label;
+  const vixl32::Register out = OutputRegister(cond);
 
-  switch (cond->InputAt(0)->GetType()) {
-    default: {
-      // Integer case.
-      if (right.IsRegister()) {
-        __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
-      } else {
-        DCHECK(right.IsConstant());
-        __ Cmp(InputRegisterAt(cond, 0),
-               CodeGenerator::GetInt32ValueOf(right.GetConstant()));
-      }
-      ExactAssemblyScope aas(GetVIXLAssembler(),
-                             3 * vixl32::kMaxInstructionSizeInBytes,
-                             CodeBufferCheckScope::kMaximumSize);
-      __ ite(ARMCondition(cond->GetCondition()));
-      __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
-      __ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0);
-      return;
-    }
-    case Primitive::kPrimLong:
-      GenerateLongComparesAndJumps(cond, &true_label, &false_label);
-      break;
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      GenerateVcmp(cond, codegen_);
-      GenerateFPJumps(cond, &true_label, &false_label);
-      break;
+  if (out.IsLow() && CanGenerateTest(cond, codegen_->GetAssembler())) {
+    const auto condition = GenerateTest(cond, false, codegen_);
+    // We use the scope because of the IT block that follows.
+    ExactAssemblyScope guard(GetVIXLAssembler(),
+                             4 * vixl32::k16BitT32InstructionSizeInBytes,
+                             CodeBufferCheckScope::kExactSize);
+
+    __ it(condition.first);
+    __ mov(condition.first, out, 1);
+    __ it(condition.second);
+    __ mov(condition.second, out, 0);
+    return;
   }
 
   // Convert the jumps into the result.
   vixl32::Label done_label;
-  vixl32::Label* final_label = codegen_->GetFinalLabel(cond, &done_label);
+  vixl32::Label* const final_label = codegen_->GetFinalLabel(cond, &done_label);
 
-  // False case: result = 0.
-  __ Bind(&false_label);
-  __ Mov(out, 0);
-  __ B(final_label);
+  if (cond->InputAt(0)->GetType() == Primitive::kPrimLong) {
+    vixl32::Label true_label, false_label;
 
-  // True case: result = 1.
-  __ Bind(&true_label);
-  __ Mov(out, 1);
+    GenerateLongComparesAndJumps(cond, &true_label, &false_label);
+
+    // False case: result = 0.
+    __ Bind(&false_label);
+    __ Mov(out, 0);
+    __ B(final_label);
+
+    // True case: result = 1.
+    __ Bind(&true_label);
+    __ Mov(out, 1);
+  } else {
+    DCHECK(CanGenerateTest(cond, codegen_->GetAssembler()));
+
+    const auto condition = GenerateTest(cond, false, codegen_);
+
+    __ Mov(LeaveFlags, out, 0);
+    __ B(condition.second, final_label, /* far_target */ false);
+    __ Mov(out, 1);
+  }
 
   if (done_label.IsReferenced()) {
     __ Bind(&done_label);
@@ -7079,14 +7108,16 @@
   uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
   uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
   uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
-  vixl32::Label done, zero;
-  vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
+  vixl32::Label done;
+  vixl32::Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
   SlowPathCodeARMVIXL* slow_path = nullptr;
 
   // Return 0 if `obj` is null.
   // avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
-    __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false);
+    DCHECK(!out.Is(obj));
+    __ Mov(out, 0);
+    __ CompareAndBranchIfZero(obj, final_label, /* far_target */ false);
   }
 
   switch (type_check_kind) {
@@ -7098,11 +7129,28 @@
                                         class_offset,
                                         maybe_temp_loc,
                                         kCompilerReadBarrierOption);
-      __ Cmp(out, cls);
       // Classes must be equal for the instanceof to succeed.
-      __ B(ne, &zero, /* far_target */ false);
-      __ Mov(out, 1);
-      __ B(final_label);
+      __ Cmp(out, cls);
+      // We speculatively set the result to false without changing the condition
+      // flags, which allows us to avoid some branching later.
+      __ Mov(LeaveFlags, out, 0);
+
+      // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+      // we check that the output is in a low register, so that a 16-bit MOV
+      // encoding can be used.
+      if (out.IsLow()) {
+        // We use the scope because of the IT block that follows.
+        ExactAssemblyScope guard(GetVIXLAssembler(),
+                                 2 * vixl32::k16BitT32InstructionSizeInBytes,
+                                 CodeBufferCheckScope::kExactSize);
+
+        __ it(eq);
+        __ mov(eq, out, 1);
+      } else {
+        __ B(ne, final_label, /* far_target */ false);
+        __ Mov(out, 1);
+      }
+
       break;
     }
 
@@ -7124,14 +7172,11 @@
                                        super_offset,
                                        maybe_temp_loc,
                                        kCompilerReadBarrierOption);
-      // If `out` is null, we use it for the result, and jump to `done`.
+      // If `out` is null, we use it for the result, and jump to the final label.
       __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
       __ Cmp(out, cls);
       __ B(ne, &loop, /* far_target */ false);
       __ Mov(out, 1);
-      if (zero.IsReferenced()) {
-        __ B(final_label);
-      }
       break;
     }
 
@@ -7154,14 +7199,38 @@
                                        super_offset,
                                        maybe_temp_loc,
                                        kCompilerReadBarrierOption);
-      __ CompareAndBranchIfNonZero(out, &loop);
-      // If `out` is null, we use it for the result, and jump to `done`.
-      __ B(final_label);
-      __ Bind(&success);
-      __ Mov(out, 1);
-      if (zero.IsReferenced()) {
+      // This is essentially a null check, but it sets the condition flags to the
+      // proper value for the code that follows the loop, i.e. not `eq`.
+      __ Cmp(out, 1);
+      __ B(hs, &loop, /* far_target */ false);
+
+      // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+      // we check that the output is in a low register, so that a 16-bit MOV
+      // encoding can be used.
+      if (out.IsLow()) {
+        // If `out` is null, we use it for the result, and the condition flags
+        // have already been set to `ne`, so the IT block that comes afterwards
+        // (and which handles the successful case) turns into a NOP (instead of
+        // overwriting `out`).
+        __ Bind(&success);
+
+        // We use the scope because of the IT block that follows.
+        ExactAssemblyScope guard(GetVIXLAssembler(),
+                                 2 * vixl32::k16BitT32InstructionSizeInBytes,
+                                 CodeBufferCheckScope::kExactSize);
+
+        // There is only one branch to the `success` label (which is bound to this
+        // IT block), and it has the same condition, `eq`, so in that case the MOV
+        // is executed.
+        __ it(eq);
+        __ mov(eq, out, 1);
+      } else {
+        // If `out` is null, we use it for the result, and jump to the final label.
         __ B(final_label);
+        __ Bind(&success);
+        __ Mov(out, 1);
       }
+
       break;
     }
 
@@ -7184,14 +7253,34 @@
                                        component_offset,
                                        maybe_temp_loc,
                                        kCompilerReadBarrierOption);
-      // If `out` is null, we use it for the result, and jump to `done`.
+      // If `out` is null, we use it for the result, and jump to the final label.
       __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
       GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-      __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false);
-      __ Bind(&exact_check);
-      __ Mov(out, 1);
-      __ B(final_label);
+      __ Cmp(out, 0);
+      // We speculatively set the result to false without changing the condition
+      // flags, which allows us to avoid some branching later.
+      __ Mov(LeaveFlags, out, 0);
+
+      // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+      // we check that the output is in a low register, so that a 16-bit MOV
+      // encoding can be used.
+      if (out.IsLow()) {
+        __ Bind(&exact_check);
+
+        // We use the scope because of the IT block that follows.
+        ExactAssemblyScope guard(GetVIXLAssembler(),
+                                 2 * vixl32::k16BitT32InstructionSizeInBytes,
+                                 CodeBufferCheckScope::kExactSize);
+
+        __ it(eq);
+        __ mov(eq, out, 1);
+      } else {
+        __ B(ne, final_label, /* far_target */ false);
+        __ Bind(&exact_check);
+        __ Mov(out, 1);
+      }
+
       break;
     }
 
@@ -7211,9 +7300,6 @@
       codegen_->AddSlowPath(slow_path);
       __ B(ne, slow_path->GetEntryLabel());
       __ Mov(out, 1);
-      if (zero.IsReferenced()) {
-        __ B(final_label);
-      }
       break;
     }
 
@@ -7242,18 +7328,10 @@
                                                                         /* is_fatal */ false);
       codegen_->AddSlowPath(slow_path);
       __ B(slow_path->GetEntryLabel());
-      if (zero.IsReferenced()) {
-        __ B(final_label);
-      }
       break;
     }
   }
 
-  if (zero.IsReferenced()) {
-    __ Bind(&zero);
-    __ Mov(out, 0);
-  }
-
   if (done.IsReferenced()) {
     __ Bind(&done);
   }
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 781027a..1e9669d 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -401,9 +401,6 @@
   void GenerateCompareTestAndBranch(HCondition* condition,
                                     vixl::aarch32::Label* true_target,
                                     vixl::aarch32::Label* false_target);
-  void GenerateFPJumps(HCondition* cond,
-                       vixl::aarch32::Label* true_label,
-                       vixl::aarch32::Label* false_label);
   void GenerateLongComparesAndJumps(HCondition* cond,
                                     vixl::aarch32::Label* true_label,
                                     vixl::aarch32::Label* false_label);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 5246dbc..c82533b 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -558,26 +558,21 @@
     return;
   }
 
-  // Make sure the frame size isn't unreasonably large. Per the various APIs
-  // it looks like it should always be less than 2GB in size, which allows
-  // us using 32-bit signed offsets from the stack pointer.
-  if (GetFrameSize() > 0x7FFFFFFF)
-    LOG(FATAL) << "Stack frame larger than 2GB";
+  // Make sure the frame size isn't unreasonably large.
+  if (GetFrameSize() > GetStackOverflowReservedBytes(kMips64)) {
+    LOG(FATAL) << "Stack frame larger than " << GetStackOverflowReservedBytes(kMips64) << " bytes";
+  }
 
   // Spill callee-saved registers.
-  // Note that their cumulative size is small and they can be indexed using
-  // 16-bit offsets.
 
-  // TODO: increment/decrement SP in one step instead of two or remove this comment.
-
-  uint32_t ofs = FrameEntrySpillSize();
+  uint32_t ofs = GetFrameSize();
   __ IncreaseFrameSize(ofs);
 
   for (int i = arraysize(kCoreCalleeSaves) - 1; i >= 0; --i) {
     GpuRegister reg = kCoreCalleeSaves[i];
     if (allocated_registers_.ContainsCoreRegister(reg)) {
       ofs -= kMips64DoublewordSize;
-      __ Sd(reg, SP, ofs);
+      __ StoreToOffset(kStoreDoubleword, reg, SP, ofs);
       __ cfi().RelOffset(DWARFReg(reg), ofs);
     }
   }
@@ -586,23 +581,16 @@
     FpuRegister reg = kFpuCalleeSaves[i];
     if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
       ofs -= kMips64DoublewordSize;
-      __ Sdc1(reg, SP, ofs);
+      __ StoreFpuToOffset(kStoreDoubleword, reg, SP, ofs);
       __ cfi().RelOffset(DWARFReg(reg), ofs);
     }
   }
 
-  // Allocate the rest of the frame and store the current method pointer
-  // at its end.
-
-  __ IncreaseFrameSize(GetFrameSize() - FrameEntrySpillSize());
-
   // Save the current method if we need it. Note that we do not
   // do this in HCurrentMethod, as the instruction might have been removed
   // in the SSA graph.
   if (RequiresCurrentMethod()) {
-    static_assert(IsInt<16>(kCurrentMethodStackOffset),
-                  "kCurrentMethodStackOffset must fit into int16_t");
-    __ Sd(kMethodRegisterArgument, SP, kCurrentMethodStackOffset);
+    __ StoreToOffset(kStoreDoubleword, kMethodRegisterArgument, SP, kCurrentMethodStackOffset);
   }
 
   if (GetGraph()->HasShouldDeoptimizeFlag()) {
@@ -615,42 +603,32 @@
   __ cfi().RememberState();
 
   if (!HasEmptyFrame()) {
-    // Deallocate the rest of the frame.
-
-    __ DecreaseFrameSize(GetFrameSize() - FrameEntrySpillSize());
-
     // Restore callee-saved registers.
-    // Note that their cumulative size is small and they can be indexed using
-    // 16-bit offsets.
 
-    // TODO: increment/decrement SP in one step instead of two or remove this comment.
-
-    uint32_t ofs = 0;
-
-    for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
-      FpuRegister reg = kFpuCalleeSaves[i];
-      if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
-        __ Ldc1(reg, SP, ofs);
-        ofs += kMips64DoublewordSize;
-        __ cfi().Restore(DWARFReg(reg));
-      }
-    }
-
-    for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) {
+    // For better instruction scheduling restore RA before other registers.
+    uint32_t ofs = GetFrameSize();
+    for (int i = arraysize(kCoreCalleeSaves) - 1; i >= 0; --i) {
       GpuRegister reg = kCoreCalleeSaves[i];
       if (allocated_registers_.ContainsCoreRegister(reg)) {
-        __ Ld(reg, SP, ofs);
-        ofs += kMips64DoublewordSize;
+        ofs -= kMips64DoublewordSize;
+        __ LoadFromOffset(kLoadDoubleword, reg, SP, ofs);
         __ cfi().Restore(DWARFReg(reg));
       }
     }
 
-    DCHECK_EQ(ofs, FrameEntrySpillSize());
-    __ DecreaseFrameSize(ofs);
+    for (int i = arraysize(kFpuCalleeSaves) - 1; i >= 0; --i) {
+      FpuRegister reg = kFpuCalleeSaves[i];
+      if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
+        ofs -= kMips64DoublewordSize;
+        __ LoadFpuFromOffset(kLoadDoubleword, reg, SP, ofs);
+        __ cfi().Restore(DWARFReg(reg));
+      }
+    }
+
+    __ DecreaseFrameSize(GetFrameSize());
   }
 
-  __ Jr(RA);
-  __ Nop();
+  __ Jic(RA, 0);
 
   __ cfi().RestoreState();
   __ cfi().DefCFAOffset(GetFrameSize());
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 4db4796..80776e8 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -723,7 +723,7 @@
            instruction_->IsArrayGet() ||
            instruction_->IsInstanceOf() ||
            instruction_->IsCheckCast() ||
-           (instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
+           (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
         << "Unexpected instruction in read barrier for heap reference slow path: "
         << instruction_->DebugName();
 
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 3f321ba..49f099f 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -744,7 +744,7 @@
            instruction_->IsArrayGet() ||
            instruction_->IsInstanceOf() ||
            instruction_->IsCheckCast() ||
-           (instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
+           (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
         << "Unexpected instruction in read barrier for heap reference slow path: "
         << instruction_->DebugName();
 
diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h
index cd95404..31cd204 100644
--- a/compiler/optimizing/codegen_test_utils.h
+++ b/compiler/optimizing/codegen_test_utils.h
@@ -74,7 +74,6 @@
   }
 
  private:
-  CodegenTargetConfig() {}
   InstructionSet isa_;
   CreateCodegenFn create_codegen_;
 };
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index e184745..01304ac 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -66,6 +66,11 @@
   return vixl::aarch32::SRegister(location.AsFpuRegisterPairLow<vixl::aarch32::SRegister>());
 }
 
+inline vixl::aarch32::SRegister HighSRegisterFrom(Location location) {
+  DCHECK(location.IsFpuRegisterPair()) << location;
+  return vixl::aarch32::SRegister(location.AsFpuRegisterPairHigh<vixl::aarch32::SRegister>());
+}
+
 inline vixl::aarch32::Register RegisterFrom(Location location) {
   DCHECK(location.IsRegister()) << location;
   return vixl::aarch32::Register(location.reg());
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 0dfae11..cc3c143 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -505,6 +505,10 @@
     StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit");
   }
 
+  void VisitDeoptimize(HDeoptimize* deoptimize) OVERRIDE {
+    StartAttributeStream("kind") << deoptimize->GetKind();
+  }
+
 #if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
   void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE {
     StartAttributeStream("kind") << instruction->GetOpKind();
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 82ee93d..9516ccb 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -29,7 +29,21 @@
  */
 class InductionVarAnalysisTest : public CommonCompilerTest {
  public:
-  InductionVarAnalysisTest() : pool_(), allocator_(&pool_) {
+  InductionVarAnalysisTest()
+      : pool_(),
+        allocator_(&pool_),
+        iva_(nullptr),
+        entry_(nullptr),
+        return_(nullptr),
+        exit_(nullptr),
+        parameter_(nullptr),
+        constant0_(nullptr),
+        constant1_(nullptr),
+        constant2_(nullptr),
+        constant7_(nullptr),
+        constant100_(nullptr),
+        constantm1_(nullptr),
+        float_constant0_(nullptr) {
     graph_ = CreateGraph(&allocator_);
   }
 
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 62f5114..19f668d 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -46,32 +46,100 @@
 
 namespace art {
 
-static constexpr size_t kMaximumNumberOfHInstructions = 32;
+// Instruction limit to control memory.
+static constexpr size_t kMaximumNumberOfTotalInstructions = 1024;
+
+// Maximum number of instructions for considering a method small,
+// which we will always try to inline if the other non-instruction limits
+// are not reached.
+static constexpr size_t kMaximumNumberOfInstructionsForSmallMethod = 3;
 
 // Limit the number of dex registers that we accumulate while inlining
 // to avoid creating large amount of nested environments.
 static constexpr size_t kMaximumNumberOfCumulatedDexRegisters = 64;
 
-// Avoid inlining within a huge method due to memory pressure.
-static constexpr size_t kMaximumCodeUnitSize = 4096;
+// Limit recursive call inlining, which do not benefit from too
+// much inlining compared to code locality.
+static constexpr size_t kMaximumNumberOfRecursiveCalls = 4;
 
 // Controls the use of inline caches in AOT mode.
 static constexpr bool kUseAOTInlineCaches = false;
 
+// We check for line numbers to make sure the DepthString implementation
+// aligns the output nicely.
+#define LOG_INTERNAL(msg) \
+  static_assert(__LINE__ > 10, "Unhandled line number"); \
+  static_assert(__LINE__ < 10000, "Unhandled line number"); \
+  VLOG(compiler) << DepthString(__LINE__) << msg
+
+#define LOG_TRY() LOG_INTERNAL("Try inlinining call: ")
+#define LOG_NOTE() LOG_INTERNAL("Note: ")
+#define LOG_SUCCESS() LOG_INTERNAL("Success: ")
+#define LOG_FAIL(stat) MaybeRecordStat(stat); LOG_INTERNAL("Fail: ")
+#define LOG_FAIL_NO_STAT() LOG_INTERNAL("Fail: ")
+
+std::string HInliner::DepthString(int line) const {
+  std::string value;
+  // Indent according to the inlining depth.
+  size_t count = depth_;
+  // Line numbers get printed in the log, so add a space if the log's line is less
+  // than 1000, and two if less than 100. 10 cannot be reached as it's the copyright.
+  if (!kIsTargetBuild) {
+    if (line < 100) {
+      value += " ";
+    }
+    if (line < 1000) {
+      value += " ";
+    }
+    // Safeguard if this file reaches more than 10000 lines.
+    DCHECK_LT(line, 10000);
+  }
+  for (size_t i = 0; i < count; ++i) {
+    value += "  ";
+  }
+  return value;
+}
+
+static size_t CountNumberOfInstructions(HGraph* graph) {
+  size_t number_of_instructions = 0;
+  for (HBasicBlock* block : graph->GetReversePostOrderSkipEntryBlock()) {
+    for (HInstructionIterator instr_it(block->GetInstructions());
+         !instr_it.Done();
+         instr_it.Advance()) {
+      ++number_of_instructions;
+    }
+  }
+  return number_of_instructions;
+}
+
+void HInliner::UpdateInliningBudget() {
+  if (total_number_of_instructions_ >= kMaximumNumberOfTotalInstructions) {
+    // Always try to inline small methods.
+    inlining_budget_ = kMaximumNumberOfInstructionsForSmallMethod;
+  } else {
+    inlining_budget_ = std::max(
+        kMaximumNumberOfInstructionsForSmallMethod,
+        kMaximumNumberOfTotalInstructions - total_number_of_instructions_);
+  }
+}
+
 void HInliner::Run() {
-  const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions();
-  if ((compiler_options.GetInlineDepthLimit() == 0)
-      || (compiler_options.GetInlineMaxCodeUnits() == 0)) {
-    return;
-  }
-  if (caller_compilation_unit_.GetCodeItem()->insns_size_in_code_units_ > kMaximumCodeUnitSize) {
-    return;
-  }
   if (graph_->IsDebuggable()) {
     // For simplicity, we currently never inline when the graph is debuggable. This avoids
     // doing some logic in the runtime to discover if a method could have been inlined.
     return;
   }
+
+  // Initialize the number of instructions for the method being compiled. Recursive calls
+  // to HInliner::Run have already updated the instruction count.
+  if (outermost_graph_ == graph_) {
+    total_number_of_instructions_ = CountNumberOfInstructions(graph_);
+  }
+
+  UpdateInliningBudget();
+  DCHECK_NE(total_number_of_instructions_, 0u);
+  DCHECK_NE(inlining_budget_, 0u);
+
   // Keep a copy of all blocks when starting the visit.
   ArenaVector<HBasicBlock*> blocks = graph_->GetReversePostOrder();
   DCHECK(!blocks.empty());
@@ -292,7 +360,18 @@
     return nullptr;
   }
   PointerSize pointer_size = caller_compilation_unit_.GetClassLinker()->GetImagePointerSize();
-  return resolved_method->GetSingleImplementation(pointer_size);
+  ArtMethod* single_impl = resolved_method->GetSingleImplementation(pointer_size);
+  if (single_impl == nullptr) {
+    return nullptr;
+  }
+  if (single_impl->IsProxyMethod()) {
+    // Proxy method is a generic invoker that's not worth
+    // devirtualizing/inlining. It also causes issues when the proxy
+    // method is in another dex file if we try to rewrite invoke-interface to
+    // invoke-virtual because a proxy method doesn't have a real dex file.
+    return nullptr;
+  }
+  return single_impl;
 }
 
 bool HInliner::TryInline(HInvoke* invoke_instruction) {
@@ -305,17 +384,18 @@
   ScopedObjectAccess soa(Thread::Current());
   uint32_t method_index = invoke_instruction->GetDexMethodIndex();
   const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
-  VLOG(compiler) << "Try inlining " << caller_dex_file.PrettyMethod(method_index);
+  LOG_TRY() << caller_dex_file.PrettyMethod(method_index);
 
-  // We can query the dex cache directly. The verifier has populated it already.
   ArtMethod* resolved_method = invoke_instruction->GetResolvedMethod();
-  ArtMethod* actual_method = nullptr;
   if (resolved_method == nullptr) {
     DCHECK(invoke_instruction->IsInvokeStaticOrDirect());
     DCHECK(invoke_instruction->AsInvokeStaticOrDirect()->IsStringInit());
-    VLOG(compiler) << "Not inlining a String.<init> method";
+    LOG_FAIL_NO_STAT() << "Not inlining a String.<init> method";
     return false;
-  } else if (invoke_instruction->IsInvokeStaticOrDirect()) {
+  }
+  ArtMethod* actual_method = nullptr;
+
+  if (invoke_instruction->IsInvokeStaticOrDirect()) {
     actual_method = resolved_method;
   } else {
     // Check if we can statically find the method.
@@ -328,6 +408,7 @@
     if (method != nullptr) {
       cha_devirtualize = true;
       actual_method = method;
+      LOG_NOTE() << "Try CHA-based inlining of " << actual_method->PrettyMethod();
     }
   }
 
@@ -390,16 +471,23 @@
       : GetInlineCacheJIT(invoke_instruction, &hs, &inline_cache);
 
   switch (inline_cache_type) {
-    case kInlineCacheNoData:
-      break;
-
-    case kInlineCacheUninitialized:
-      VLOG(compiler) << "Interface or virtual call to "
-                     << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex())
-                     << " is not hit and not inlined";
+    case kInlineCacheNoData: {
+      LOG_FAIL_NO_STAT()
+          << "Interface or virtual call to "
+          << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex())
+          << " could not be statically determined";
       return false;
+    }
 
-    case kInlineCacheMonomorphic:
+    case kInlineCacheUninitialized: {
+      LOG_FAIL_NO_STAT()
+          << "Interface or virtual call to "
+          << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex())
+          << " is not hit and not inlined";
+      return false;
+    }
+
+    case kInlineCacheMonomorphic: {
       MaybeRecordStat(kMonomorphicCall);
       if (outermost_graph_->IsCompilingOsr()) {
         // If we are compiling OSR, we pretend this call is polymorphic, as we may come from the
@@ -408,23 +496,29 @@
       } else {
         return TryInlineMonomorphicCall(invoke_instruction, resolved_method, inline_cache);
       }
+    }
 
-    case kInlineCachePolymorphic:
+    case kInlineCachePolymorphic: {
       MaybeRecordStat(kPolymorphicCall);
       return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache);
+    }
 
-    case kInlineCacheMegamorphic:
-      VLOG(compiler) << "Interface or virtual call to "
-                     << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex())
-                     << " is megamorphic and not inlined";
+    case kInlineCacheMegamorphic: {
+      LOG_FAIL_NO_STAT()
+          << "Interface or virtual call to "
+          << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex())
+          << " is megamorphic and not inlined";
       MaybeRecordStat(kMegamorphicCall);
       return false;
+    }
 
-    case kInlineCacheMissingTypes:
-      VLOG(compiler) << "Interface or virtual call to "
-                     << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex())
-                     << " is missing types and not inlined";
+    case kInlineCacheMissingTypes: {
+      LOG_FAIL_NO_STAT()
+          << "Interface or virtual call to "
+          << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex())
+          << " is missing types and not inlined";
       return false;
+    }
   }
   UNREACHABLE();
 }
@@ -587,9 +681,10 @@
   dex::TypeIndex class_index = FindClassIndexIn(
       GetMonomorphicType(classes), caller_compilation_unit_);
   if (!class_index.IsValid()) {
-    VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method)
-                   << " from inline cache is not inlined because its class is not"
-                   << " accessible to the caller";
+    LOG_FAIL(kNotInlinedDexCache)
+        << "Call to " << ArtMethod::PrettyMethod(resolved_method)
+        << " from inline cache is not inlined because its class is not"
+        << " accessible to the caller";
     return false;
   }
 
@@ -603,6 +698,7 @@
     resolved_method = GetMonomorphicType(classes)->FindVirtualMethodForVirtual(
         resolved_method, pointer_size);
   }
+  LOG_NOTE() << "Try inline monomorphic call to " << resolved_method->PrettyMethod();
   DCHECK(resolved_method != nullptr);
   HInstruction* receiver = invoke_instruction->InputAt(0);
   HInstruction* cursor = invoke_instruction->GetPrevious();
@@ -646,7 +742,8 @@
       HShouldDeoptimizeFlag(graph_->GetArena(), dex_pc);
   HInstruction* compare = new (graph_->GetArena()) HNotEqual(
       deopt_flag, graph_->GetIntConstant(0, dex_pc));
-  HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(compare, dex_pc);
+  HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(
+      graph_->GetArena(), compare, HDeoptimize::Kind::kInline, dex_pc);
 
   if (cursor != nullptr) {
     bb_cursor->InsertInstructionAfter(deopt_flag, cursor);
@@ -710,9 +807,16 @@
   bb_cursor->InsertInstructionAfter(compare, load_class);
   if (with_deoptimization) {
     HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
-        compare, invoke_instruction->GetDexPc());
+        graph_->GetArena(),
+        compare,
+        receiver,
+        HDeoptimize::Kind::kInline,
+        invoke_instruction->GetDexPc());
     bb_cursor->InsertInstructionAfter(deoptimize, compare);
     deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+    DCHECK_EQ(invoke_instruction->InputAt(0), receiver);
+    receiver->ReplaceUsesDominatedBy(deoptimize, deoptimize);
+    deoptimize->SetReferenceTypeInfo(receiver->GetReferenceTypeInfo());
   }
   return compare;
 }
@@ -752,6 +856,7 @@
 
     dex::TypeIndex class_index = FindClassIndexIn(handle.Get(), caller_compilation_unit_);
     HInstruction* return_replacement = nullptr;
+    LOG_NOTE() << "Try inline polymorphic call to " << method->PrettyMethod();
     if (!class_index.IsValid() ||
         !TryBuildAndInline(invoke_instruction,
                            method,
@@ -761,8 +866,8 @@
     } else {
       one_target_inlined = true;
 
-      VLOG(compiler) << "Polymorphic call to " << ArtMethod::PrettyMethod(resolved_method)
-                     << " has inlined " << ArtMethod::PrettyMethod(method);
+      LOG_SUCCESS() << "Polymorphic call to " << ArtMethod::PrettyMethod(resolved_method)
+                    << " has inlined " << ArtMethod::PrettyMethod(method);
 
       // If we have inlined all targets before, and this receiver is the last seen,
       // we deoptimize instead of keeping the original invoke instruction.
@@ -796,9 +901,10 @@
   }
 
   if (!one_target_inlined) {
-    VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method)
-                   << " from inline cache is not inlined because none"
-                   << " of its targets could be inlined";
+    LOG_FAIL_NO_STAT()
+        << "Call to " << ArtMethod::PrettyMethod(resolved_method)
+        << " from inline cache is not inlined because none"
+        << " of its targets could be inlined";
     return false;
   }
 
@@ -932,9 +1038,6 @@
       actual_method = new_method;
     } else if (actual_method != new_method) {
       // Different methods, bailout.
-      VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method)
-                     << " from inline cache is not inlined because it resolves"
-                     << " to different methods";
       return false;
     }
   }
@@ -988,13 +1091,19 @@
     CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction);
   } else {
     HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
-        compare, invoke_instruction->GetDexPc());
+        graph_->GetArena(),
+        compare,
+        receiver,
+        HDeoptimize::Kind::kInline,
+        invoke_instruction->GetDexPc());
     bb_cursor->InsertInstructionAfter(deoptimize, compare);
     deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
     if (return_replacement != nullptr) {
       invoke_instruction->ReplaceWith(return_replacement);
     }
+    receiver->ReplaceUsesDominatedBy(deoptimize, deoptimize);
     invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+    deoptimize->SetReferenceTypeInfo(receiver->GetReferenceTypeInfo());
   }
 
   // Run type propagation to get the guard typed.
@@ -1007,6 +1116,7 @@
 
   MaybeRecordStat(kInlinedPolymorphicCall);
 
+  LOG_SUCCESS() << "Inlined same polymorphic target " << actual_method->PrettyMethod();
   return true;
 }
 
@@ -1021,11 +1131,23 @@
   HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
   if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) {
     if (invoke_instruction->IsInvokeInterface()) {
+      DCHECK(!method->IsProxyMethod());
       // Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always
       // better than an invoke-interface because:
       // 1) In the best case, the interface call has one more indirection (to fetch the IMT).
       // 2) We will not go to the conflict trampoline with an invoke-virtual.
       // TODO: Consider sharpening once it is not dependent on the compiler driver.
+
+      if (method->IsDefault() && !method->IsCopied()) {
+        // Changing to invoke-virtual cannot be done on an original default method
+        // since it's not in any vtable. Devirtualization by exact type/inline-cache
+        // always uses a method in the iftable which is never an original default
+        // method.
+        // On the other hand, inlining an original default method by CHA is fine.
+        DCHECK(cha_devirtualize);
+        return false;
+      }
+
       const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
       uint32_t dex_method_index = FindMethodIndexIn(
           method, caller_dex_file, invoke_instruction->GetDexMethodIndex());
@@ -1076,13 +1198,34 @@
   return true;
 }
 
+size_t HInliner::CountRecursiveCallsOf(ArtMethod* method) const {
+  const HInliner* current = this;
+  size_t count = 0;
+  do {
+    if (current->graph_->GetArtMethod() == method) {
+      ++count;
+    }
+    current = current->parent_;
+  } while (current != nullptr);
+  return count;
+}
+
 bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
                                  ArtMethod* method,
                                  ReferenceTypeInfo receiver_type,
                                  HInstruction** return_replacement) {
   if (method->IsProxyMethod()) {
-    VLOG(compiler) << "Method " << method->PrettyMethod()
-                   << " is not inlined because of unimplemented inline support for proxy methods.";
+    LOG_FAIL(kNotInlinedProxy)
+        << "Method " << method->PrettyMethod()
+        << " is not inlined because of unimplemented inline support for proxy methods.";
+    return false;
+  }
+
+  if (CountRecursiveCallsOf(method) > kMaximumNumberOfRecursiveCalls) {
+    LOG_FAIL(kNotInlinedRecursiveBudget)
+        << "Method "
+        << method->PrettyMethod()
+        << " is not inlined because it has reached its recursive call budget.";
     return false;
   }
 
@@ -1091,15 +1234,16 @@
   if (!compiler_driver_->MayInline(method->GetDexFile(),
                                    outer_compilation_unit_.GetDexFile())) {
     if (TryPatternSubstitution(invoke_instruction, method, return_replacement)) {
-      VLOG(compiler) << "Successfully replaced pattern of invoke "
-                     << method->PrettyMethod();
+      LOG_SUCCESS() << "Successfully replaced pattern of invoke "
+                    << method->PrettyMethod();
       MaybeRecordStat(kReplacedInvokeWithSimplePattern);
       return true;
     }
-    VLOG(compiler) << "Won't inline " << method->PrettyMethod() << " in "
-                   << outer_compilation_unit_.GetDexFile()->GetLocation() << " ("
-                   << caller_compilation_unit_.GetDexFile()->GetLocation() << ") from "
-                   << method->GetDexFile()->GetLocation();
+    LOG_FAIL(kNotInlinedWont)
+        << "Won't inline " << method->PrettyMethod() << " in "
+        << outer_compilation_unit_.GetDexFile()->GetLocation() << " ("
+        << caller_compilation_unit_.GetDexFile()->GetLocation() << ") from "
+        << method->GetDexFile()->GetLocation();
     return false;
   }
 
@@ -1108,30 +1252,32 @@
   const DexFile::CodeItem* code_item = method->GetCodeItem();
 
   if (code_item == nullptr) {
-    VLOG(compiler) << "Method " << method->PrettyMethod()
-                   << " is not inlined because it is native";
+    LOG_FAIL_NO_STAT()
+        << "Method " << method->PrettyMethod() << " is not inlined because it is native";
     return false;
   }
 
   size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits();
   if (code_item->insns_size_in_code_units_ > inline_max_code_units) {
-    VLOG(compiler) << "Method " << method->PrettyMethod()
-                   << " is too big to inline: "
-                   << code_item->insns_size_in_code_units_
-                   << " > "
-                   << inline_max_code_units;
+    LOG_FAIL(kNotInlinedCodeItem)
+        << "Method " << method->PrettyMethod()
+        << " is not inlined because its code item is too big: "
+        << code_item->insns_size_in_code_units_
+        << " > "
+        << inline_max_code_units;
     return false;
   }
 
   if (code_item->tries_size_ != 0) {
-    VLOG(compiler) << "Method " << method->PrettyMethod()
-                   << " is not inlined because of try block";
+    LOG_FAIL(kNotInlinedTryCatch)
+        << "Method " << method->PrettyMethod() << " is not inlined because of try block";
     return false;
   }
 
   if (!method->IsCompilable()) {
-    VLOG(compiler) << "Method " << method->PrettyMethod()
-                   << " has soft failures un-handled by the compiler, so it cannot be inlined";
+    LOG_FAIL(kNotInlinedNotVerified)
+        << "Method " << method->PrettyMethod()
+        << " has soft failures un-handled by the compiler, so it cannot be inlined";
   }
 
   if (!method->GetDeclaringClass()->IsVerified()) {
@@ -1139,8 +1285,9 @@
     if (Runtime::Current()->UseJitCompilation() ||
         !compiler_driver_->IsMethodVerifiedWithoutFailures(
             method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) {
-      VLOG(compiler) << "Method " << method->PrettyMethod()
-                     << " couldn't be verified, so it cannot be inlined";
+      LOG_FAIL(kNotInlinedNotVerified)
+          << "Method " << method->PrettyMethod()
+          << " couldn't be verified, so it cannot be inlined";
       return false;
     }
   }
@@ -1149,9 +1296,9 @@
       invoke_instruction->AsInvokeStaticOrDirect()->IsStaticWithImplicitClinitCheck()) {
     // Case of a static method that cannot be inlined because it implicitly
     // requires an initialization check of its declaring class.
-    VLOG(compiler) << "Method " << method->PrettyMethod()
-                   << " is not inlined because it is static and requires a clinit"
-                   << " check that cannot be emitted due to Dex cache limitations";
+    LOG_FAIL(kNotInlinedDexCache) << "Method " << method->PrettyMethod()
+             << " is not inlined because it is static and requires a clinit"
+             << " check that cannot be emitted due to Dex cache limitations";
     return false;
   }
 
@@ -1160,7 +1307,7 @@
     return false;
   }
 
-  VLOG(compiler) << "Successfully inlined " << method->PrettyMethod();
+  LOG_SUCCESS() << method->PrettyMethod();
   MaybeRecordStat(kInlinedInvoke);
   return true;
 }
@@ -1448,15 +1595,17 @@
                         handles_);
 
   if (builder.BuildGraph() != kAnalysisSuccess) {
-    VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                   << " could not be built, so cannot be inlined";
+    LOG_FAIL(kNotInlinedCannotBuild)
+        << "Method " << callee_dex_file.PrettyMethod(method_index)
+        << " could not be built, so cannot be inlined";
     return false;
   }
 
   if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph,
                                                   compiler_driver_->GetInstructionSet())) {
-    VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                   << " cannot be inlined because of the register allocator";
+    LOG_FAIL(kNotInlinedRegisterAllocator)
+        << "Method " << callee_dex_file.PrettyMethod(method_index)
+        << " cannot be inlined because of the register allocator";
     return false;
   }
 
@@ -1503,15 +1652,13 @@
                              /* is_first_run */ false).Run();
   }
 
-  size_t number_of_instructions_budget = kMaximumNumberOfHInstructions;
-  size_t number_of_inlined_instructions =
-      RunOptimizations(callee_graph, code_item, dex_compilation_unit);
-  number_of_instructions_budget += number_of_inlined_instructions;
+  RunOptimizations(callee_graph, code_item, dex_compilation_unit);
 
   HBasicBlock* exit_block = callee_graph->GetExitBlock();
   if (exit_block == nullptr) {
-    VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                   << " could not be inlined because it has an infinite loop";
+    LOG_FAIL(kNotInlinedInfiniteLoop)
+        << "Method " << callee_dex_file.PrettyMethod(method_index)
+        << " could not be inlined because it has an infinite loop";
     return false;
   }
 
@@ -1520,15 +1667,17 @@
     if (predecessor->GetLastInstruction()->IsThrow()) {
       if (invoke_instruction->GetBlock()->IsTryBlock()) {
         // TODO(ngeoffray): Support adding HTryBoundary in Hgraph::InlineInto.
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " could not be inlined because one branch always throws and"
-                       << " caller is in a try/catch block";
+        LOG_FAIL(kNotInlinedTryCatch)
+            << "Method " << callee_dex_file.PrettyMethod(method_index)
+            << " could not be inlined because one branch always throws and"
+            << " caller is in a try/catch block";
         return false;
       } else if (graph_->GetExitBlock() == nullptr) {
         // TODO(ngeoffray): Support adding HExit in the caller graph.
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " could not be inlined because one branch always throws and"
-                       << " caller does not have an exit block";
+        LOG_FAIL(kNotInlinedInfiniteLoop)
+            << "Method " << callee_dex_file.PrettyMethod(method_index)
+            << " could not be inlined because one branch always throws and"
+            << " caller does not have an exit block";
         return false;
       } else if (graph_->HasIrreducibleLoops()) {
         // TODO(ngeoffray): Support re-computing loop information to graphs with
@@ -1544,32 +1693,31 @@
   }
 
   if (!has_one_return) {
-    VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                   << " could not be inlined because it always throws";
+    LOG_FAIL(kNotInlinedAlwaysThrows)
+        << "Method " << callee_dex_file.PrettyMethod(method_index)
+        << " could not be inlined because it always throws";
     return false;
   }
 
   size_t number_of_instructions = 0;
-
-  bool can_inline_environment =
-      total_number_of_dex_registers_ < kMaximumNumberOfCumulatedDexRegisters;
-
   // Skip the entry block, it does not contain instructions that prevent inlining.
   for (HBasicBlock* block : callee_graph->GetReversePostOrderSkipEntryBlock()) {
     if (block->IsLoopHeader()) {
       if (block->GetLoopInformation()->IsIrreducible()) {
         // Don't inline methods with irreducible loops, they could prevent some
         // optimizations to run.
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " could not be inlined because it contains an irreducible loop";
+        LOG_FAIL(kNotInlinedIrreducibleLoop)
+            << "Method " << callee_dex_file.PrettyMethod(method_index)
+            << " could not be inlined because it contains an irreducible loop";
         return false;
       }
       if (!block->GetLoopInformation()->HasExitEdge()) {
         // Don't inline methods with loops without exit, since they cause the
         // loop information to be computed incorrectly when updating after
         // inlining.
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " could not be inlined because it contains a loop with no exit";
+        LOG_FAIL(kNotInlinedLoopWithoutExit)
+            << "Method " << callee_dex_file.PrettyMethod(method_index)
+            << " could not be inlined because it contains a loop with no exit";
         return false;
       }
     }
@@ -1577,34 +1725,39 @@
     for (HInstructionIterator instr_it(block->GetInstructions());
          !instr_it.Done();
          instr_it.Advance()) {
-      if (number_of_instructions++ == number_of_instructions_budget) {
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " is not inlined because its caller has reached"
-                       << " its instruction budget limit.";
+      if (++number_of_instructions >= inlining_budget_) {
+        LOG_FAIL(kNotInlinedInstructionBudget)
+            << "Method " << callee_dex_file.PrettyMethod(method_index)
+            << " is not inlined because the outer method has reached"
+            << " its instruction budget limit.";
         return false;
       }
       HInstruction* current = instr_it.Current();
-      if (!can_inline_environment && current->NeedsEnvironment()) {
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " is not inlined because its caller has reached"
-                       << " its environment budget limit.";
+      if (current->NeedsEnvironment() &&
+          (total_number_of_dex_registers_ >= kMaximumNumberOfCumulatedDexRegisters)) {
+        LOG_FAIL(kNotInlinedEnvironmentBudget)
+            << "Method " << callee_dex_file.PrettyMethod(method_index)
+            << " is not inlined because its caller has reached"
+            << " its environment budget limit.";
         return false;
       }
 
       if (current->NeedsEnvironment() &&
           !CanEncodeInlinedMethodInStackMap(*caller_compilation_unit_.GetDexFile(),
                                             resolved_method)) {
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " could not be inlined because " << current->DebugName()
-                       << " needs an environment, is in a different dex file"
-                       << ", and cannot be encoded in the stack maps.";
+        LOG_FAIL(kNotInlinedStackMaps)
+            << "Method " << callee_dex_file.PrettyMethod(method_index)
+            << " could not be inlined because " << current->DebugName()
+            << " needs an environment, is in a different dex file"
+            << ", and cannot be encoded in the stack maps.";
         return false;
       }
 
       if (!same_dex_file && current->NeedsDexCacheOfDeclaringClass()) {
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " could not be inlined because " << current->DebugName()
-                       << " it is in a different dex file and requires access to the dex cache";
+        LOG_FAIL(kNotInlinedDexCache)
+            << "Method " << callee_dex_file.PrettyMethod(method_index)
+            << " could not be inlined because " << current->DebugName()
+            << " it is in a different dex file and requires access to the dex cache";
         return false;
       }
 
@@ -1613,21 +1766,24 @@
           current->IsUnresolvedStaticFieldSet() ||
           current->IsUnresolvedInstanceFieldSet()) {
         // Entrypoint for unresolved fields does not handle inlined frames.
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " could not be inlined because it is using an unresolved"
-                       << " entrypoint";
+        LOG_FAIL(kNotInlinedUnresolvedEntrypoint)
+            << "Method " << callee_dex_file.PrettyMethod(method_index)
+            << " could not be inlined because it is using an unresolved"
+            << " entrypoint";
         return false;
       }
     }
   }
-  number_of_inlined_instructions_ += number_of_instructions;
-
   DCHECK_EQ(caller_instruction_counter, graph_->GetCurrentInstructionId())
       << "No instructions can be added to the outer graph while inner graph is being built";
 
+  // Inline the callee graph inside the caller graph.
   const int32_t callee_instruction_counter = callee_graph->GetCurrentInstructionId();
   graph_->SetCurrentInstructionId(callee_instruction_counter);
   *return_replacement = callee_graph->InlineInto(graph_, invoke_instruction);
+  // Update our budget for other inlining attempts in `caller_graph`.
+  total_number_of_instructions_ += number_of_instructions;
+  UpdateInliningBudget();
 
   DCHECK_EQ(callee_instruction_counter, callee_graph->GetCurrentInstructionId())
       << "No instructions can be added to the inner graph during inlining into the outer graph";
@@ -1640,9 +1796,9 @@
   return true;
 }
 
-size_t HInliner::RunOptimizations(HGraph* callee_graph,
-                                  const DexFile::CodeItem* code_item,
-                                  const DexCompilationUnit& dex_compilation_unit) {
+void HInliner::RunOptimizations(HGraph* callee_graph,
+                                const DexFile::CodeItem* code_item,
+                                const DexCompilationUnit& dex_compilation_unit) {
   // Note: if the outermost_graph_ is being compiled OSR, we should not run any
   // optimization that could lead to a HDeoptimize. The following optimizations do not.
   HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner");
@@ -1664,23 +1820,37 @@
     optimization->Run();
   }
 
-  size_t number_of_inlined_instructions = 0u;
-  if (depth_ + 1 < compiler_driver_->GetCompilerOptions().GetInlineDepthLimit()) {
-    HInliner inliner(callee_graph,
-                     outermost_graph_,
-                     codegen_,
-                     outer_compilation_unit_,
-                     dex_compilation_unit,
-                     compiler_driver_,
-                     handles_,
-                     inline_stats_,
-                     total_number_of_dex_registers_ + code_item->registers_size_,
-                     depth_ + 1);
-    inliner.Run();
-    number_of_inlined_instructions += inliner.number_of_inlined_instructions_;
+  // Bail early for pathological cases on the environment (for example recursive calls,
+  // or too large environment).
+  if (total_number_of_dex_registers_ >= kMaximumNumberOfCumulatedDexRegisters) {
+    LOG_NOTE() << "Calls in " << callee_graph->GetArtMethod()->PrettyMethod()
+             << " will not be inlined because the outer method has reached"
+             << " its environment budget limit.";
+    return;
   }
 
-  return number_of_inlined_instructions;
+  // Bail early if we know we already are over the limit.
+  size_t number_of_instructions = CountNumberOfInstructions(callee_graph);
+  if (number_of_instructions > inlining_budget_) {
+    LOG_NOTE() << "Calls in " << callee_graph->GetArtMethod()->PrettyMethod()
+             << " will not be inlined because the outer method has reached"
+             << " its instruction budget limit. " << number_of_instructions;
+    return;
+  }
+
+  HInliner inliner(callee_graph,
+                   outermost_graph_,
+                   codegen_,
+                   outer_compilation_unit_,
+                   dex_compilation_unit,
+                   compiler_driver_,
+                   handles_,
+                   inline_stats_,
+                   total_number_of_dex_registers_ + code_item->registers_size_,
+                   total_number_of_instructions_ + number_of_instructions,
+                   this,
+                   depth_ + 1);
+  inliner.Run();
 }
 
 static bool IsReferenceTypeRefinement(ReferenceTypeInfo declared_rti,
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index a032042..9e4685c 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -42,7 +42,9 @@
            VariableSizedHandleScope* handles,
            OptimizingCompilerStats* stats,
            size_t total_number_of_dex_registers,
-           size_t depth)
+           size_t total_number_of_instructions,
+           HInliner* parent,
+           size_t depth = 0)
       : HOptimization(outer_graph, kInlinerPassName, stats),
         outermost_graph_(outermost_graph),
         outer_compilation_unit_(outer_compilation_unit),
@@ -50,8 +52,10 @@
         codegen_(codegen),
         compiler_driver_(compiler_driver),
         total_number_of_dex_registers_(total_number_of_dex_registers),
+        total_number_of_instructions_(total_number_of_instructions),
+        parent_(parent),
         depth_(depth),
-        number_of_inlined_instructions_(0),
+        inlining_budget_(0),
         handles_(handles),
         inline_stats_(nullptr) {}
 
@@ -95,10 +99,10 @@
                                HInstruction** return_replacement);
 
   // Run simple optimizations on `callee_graph`.
-  // Returns the number of inlined instructions.
-  size_t RunOptimizations(HGraph* callee_graph,
-                          const DexFile::CodeItem* code_item,
-                          const DexCompilationUnit& dex_compilation_unit);
+  void RunOptimizations(HGraph* callee_graph,
+                        const DexFile::CodeItem* code_item,
+                        const DexCompilationUnit& dex_compilation_unit)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Try to recognize known simple patterns and replace invoke call with appropriate instructions.
   bool TryPatternSubstitution(HInvoke* invoke_instruction,
@@ -259,14 +263,30 @@
                                                 HInstruction* return_replacement,
                                                 HInstruction* invoke_instruction);
 
+  // Update the inlining budget based on `total_number_of_instructions_`.
+  void UpdateInliningBudget();
+
+  // Count the number of calls of `method` being inlined recursively.
+  size_t CountRecursiveCallsOf(ArtMethod* method) const;
+
+  // Pretty-print for spaces during logging.
+  std::string DepthString(int line) const;
+
   HGraph* const outermost_graph_;
   const DexCompilationUnit& outer_compilation_unit_;
   const DexCompilationUnit& caller_compilation_unit_;
   CodeGenerator* const codegen_;
   CompilerDriver* const compiler_driver_;
   const size_t total_number_of_dex_registers_;
+  size_t total_number_of_instructions_;
+
+  // The 'parent' inliner, that means the inlinigng optimization that requested
+  // `graph_` to be inlined.
+  const HInliner* const parent_;
   const size_t depth_;
-  size_t number_of_inlined_instructions_;
+
+  // The budget left for inlining, in number of instructions.
+  size_t inlining_budget_;
   VariableSizedHandleScope* const handles_;
 
   // Used to record stats about optimizations on the inlined graph.
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 17421fc..60790e5 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -2132,6 +2132,9 @@
   if (cond->IsConstant()) {
     if (cond->AsIntConstant()->IsFalse()) {
       // Never deopt: instruction can be removed.
+      if (deoptimize->GuardsAnInput()) {
+        deoptimize->ReplaceWith(deoptimize->GuardedInput());
+      }
       deoptimize->GetBlock()->RemoveInstruction(deoptimize);
     } else {
       // Always deopt.
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index b25bad7..0d933ea 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -39,6 +39,7 @@
 using helpers::LocationFrom;
 using helpers::LowRegisterFrom;
 using helpers::LowSRegisterFrom;
+using helpers::HighSRegisterFrom;
 using helpers::OutputDRegister;
 using helpers::OutputSRegister;
 using helpers::OutputRegister;
@@ -794,6 +795,58 @@
   __ Vrintn(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
 }
 
+void IntrinsicLocationsBuilderARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
+  if (features_.HasARMv8AInstructions()) {
+    LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                              LocationSummary::kNoCall,
+                                                              kIntrinsified);
+    locations->SetInAt(0, Location::RequiresFpuRegister());
+    locations->SetOut(Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresFpuRegister());
+  }
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
+  DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
+
+  ArmVIXLAssembler* assembler = GetAssembler();
+  vixl32::SRegister in_reg = InputSRegisterAt(invoke, 0);
+  vixl32::Register out_reg = OutputRegister(invoke);
+  vixl32::SRegister temp1 = LowSRegisterFrom(invoke->GetLocations()->GetTemp(0));
+  vixl32::SRegister temp2 = HighSRegisterFrom(invoke->GetLocations()->GetTemp(0));
+  vixl32::Label done;
+  vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &done);
+
+  // Round to nearest integer, ties away from zero.
+  __ Vcvta(S32, F32, temp1, in_reg);
+  __ Vmov(out_reg, temp1);
+
+  // For positive, zero or NaN inputs, rounding is done.
+  __ Cmp(out_reg, 0);
+  __ B(ge, final_label, /* far_target */ false);
+
+  // Handle input < 0 cases.
+  // If input is negative but not a tie, previous result (round to nearest) is valid.
+  // If input is a negative tie, change rounding direction to positive infinity, out_reg += 1.
+  __ Vrinta(F32, F32, temp1, in_reg);
+  __ Vmov(temp2, 0.5);
+  __ Vsub(F32, temp1, in_reg, temp1);
+  __ Vcmp(F32, temp1, temp2);
+  __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
+  {
+    // Use ExactAsemblyScope here because we are using IT.
+    ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(),
+                                2 * kMaxInstructionSizeInBytes,
+                                CodeBufferCheckScope::kMaximumSize);
+    __ it(eq);
+    __ add(eq, out_reg, out_reg, 1);
+  }
+
+  if (done.IsReferenced()) {
+    __ Bind(&done);
+  }
+}
+
 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
   CreateIntToIntLocations(arena_, invoke);
 }
@@ -3100,7 +3153,6 @@
 }
 
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble)   // Could be done by changing rounding mode, maybe?
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundFloat)    // Could be done by changing rounding mode, maybe?
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong)     // High register pressure.
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerHighestOneBit)
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 5bcfa4c..8d15f78 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -28,7 +28,18 @@
  */
 class LICMTest : public CommonCompilerTest {
  public:
-  LICMTest() : pool_(), allocator_(&pool_) {
+  LICMTest()
+      : pool_(),
+        allocator_(&pool_),
+        entry_(nullptr),
+        loop_preheader_(nullptr),
+        loop_header_(nullptr),
+        loop_body_(nullptr),
+        return_(nullptr),
+        exit_(nullptr),
+        parameter_(nullptr),
+        int_constant_(nullptr),
+        float_constant_(nullptr) {
     graph_ = CreateGraph(&allocator_);
   }
 
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ec706e6..caada8b 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1088,6 +1088,19 @@
   DCHECK(env_uses_.empty());
 }
 
+void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement) {
+  const HUseList<HInstruction*>& uses = GetUses();
+  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+    HInstruction* user = it->GetUser();
+    size_t index = it->GetIndex();
+    // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
+    ++it;
+    if (dominator->StrictlyDominates(user)) {
+      user->ReplaceInput(replacement, index);
+    }
+  }
+}
+
 void HInstruction::ReplaceInput(HInstruction* replacement, size_t index) {
   HUserRecord<HInstruction*> input_use = InputRecordAt(index);
   if (input_use.GetInstruction() == replacement) {
@@ -1323,6 +1336,18 @@
   }
 }
 
+std::ostream& operator<<(std::ostream& os, const HDeoptimize::Kind& rhs) {
+  switch (rhs) {
+    case HDeoptimize::Kind::kBCE:
+      return os << "bce";
+    case HDeoptimize::Kind::kInline:
+      return os << "inline";
+    default:
+      LOG(FATAL) << "Unknown Deoptimization kind: " << static_cast<int>(rhs);
+      UNREACHABLE();
+  }
+}
+
 bool HCondition::IsBeforeWhenDisregardMoves(HInstruction* instruction) const {
   return this == instruction->GetPreviousDisregardingMoves();
 }
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 6881d8f..5f5a28c 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -341,6 +341,7 @@
         cached_long_constants_(std::less<int64_t>(), arena->Adapter(kArenaAllocConstantsMap)),
         cached_double_constants_(std::less<int64_t>(), arena->Adapter(kArenaAllocConstantsMap)),
         cached_current_method_(nullptr),
+        art_method_(nullptr),
         inexact_object_rti_(ReferenceTypeInfo::CreateInvalid()),
         osr_(osr),
         cha_single_implementation_list_(arena->Adapter(kArenaAllocCHA)) {
@@ -2080,6 +2081,7 @@
   void SetLocations(LocationSummary* locations) { locations_ = locations; }
 
   void ReplaceWith(HInstruction* instruction);
+  void ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement);
   void ReplaceInput(HInstruction* replacement, size_t index);
 
   // This is almost the same as doing `ReplaceWith()`. But in this helper, the
@@ -2943,28 +2945,97 @@
 };
 
 // Deoptimize to interpreter, upon checking a condition.
-class HDeoptimize FINAL : public HTemplateInstruction<1> {
+class HDeoptimize FINAL : public HVariableInputSizeInstruction {
  public:
-  // We set CanTriggerGC to prevent any intermediate address to be live
-  // at the point of the `HDeoptimize`.
-  HDeoptimize(HInstruction* cond, uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
+  enum class Kind {
+    kBCE,
+    kInline,
+    kLast = kInline
+  };
+
+  // Use this constructor when the `HDeoptimize` acts as a barrier, where no code can move
+  // across.
+  HDeoptimize(ArenaAllocator* arena, HInstruction* cond, Kind kind, uint32_t dex_pc)
+      : HVariableInputSizeInstruction(
+            SideEffects::All(),
+            dex_pc,
+            arena,
+            /* number_of_inputs */ 1,
+            kArenaAllocMisc) {
+    SetPackedFlag<kFieldCanBeMoved>(false);
+    SetPackedField<DeoptimizeKindField>(kind);
     SetRawInputAt(0, cond);
   }
 
-  bool CanBeMoved() const OVERRIDE { return true; }
-  bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
-    return true;
+  // Use this constructor when the `HDeoptimize` guards an instruction, and any user
+  // that relies on the deoptimization to pass should have its input be the `HDeoptimize`
+  // instead of `guard`.
+  // We set CanTriggerGC to prevent any intermediate address to be live
+  // at the point of the `HDeoptimize`.
+  HDeoptimize(ArenaAllocator* arena,
+              HInstruction* cond,
+              HInstruction* guard,
+              Kind kind,
+              uint32_t dex_pc)
+      : HVariableInputSizeInstruction(
+            SideEffects::CanTriggerGC(),
+            dex_pc,
+            arena,
+            /* number_of_inputs */ 2,
+            kArenaAllocMisc) {
+    SetPackedFlag<kFieldCanBeMoved>(true);
+    SetPackedField<DeoptimizeKindField>(kind);
+    SetRawInputAt(0, cond);
+    SetRawInputAt(1, guard);
   }
+
+  bool CanBeMoved() const OVERRIDE { return GetPackedFlag<kFieldCanBeMoved>(); }
+
+  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+    return (other->CanBeMoved() == CanBeMoved()) && (other->AsDeoptimize()->GetKind() == GetKind());
+  }
+
   bool NeedsEnvironment() const OVERRIDE { return true; }
+
   bool CanThrow() const OVERRIDE { return true; }
 
+  Kind GetKind() const { return GetPackedField<DeoptimizeKindField>(); }
+
+  Primitive::Type GetType() const OVERRIDE {
+    return GuardsAnInput() ? GuardedInput()->GetType() : Primitive::kPrimVoid;
+  }
+
+  bool GuardsAnInput() const {
+    return InputCount() == 2;
+  }
+
+  HInstruction* GuardedInput() const {
+    DCHECK(GuardsAnInput());
+    return InputAt(1);
+  }
+
+  void RemoveGuard() {
+    RemoveInputAt(1);
+  }
+
   DECLARE_INSTRUCTION(Deoptimize);
 
  private:
+  static constexpr size_t kFieldCanBeMoved = kNumberOfGenericPackedBits;
+  static constexpr size_t kFieldDeoptimizeKind = kNumberOfGenericPackedBits + 1;
+  static constexpr size_t kFieldDeoptimizeKindSize =
+      MinimumBitsToStore(static_cast<size_t>(Kind::kLast));
+  static constexpr size_t kNumberOfDeoptimizePackedBits =
+      kFieldDeoptimizeKind + kFieldDeoptimizeKindSize;
+  static_assert(kNumberOfDeoptimizePackedBits <= kMaxNumberOfPackedBits,
+                "Too many packed fields.");
+  using DeoptimizeKindField = BitField<Kind, kFieldDeoptimizeKind, kFieldDeoptimizeKindSize>;
+
   DISALLOW_COPY_AND_ASSIGN(HDeoptimize);
 };
 
+std::ostream& operator<<(std::ostream& os, const HDeoptimize::Kind& rhs);
+
 // Represents a should_deoptimize flag. Currently used for CHA-based devirtualization.
 // The compiled code checks this flag value in a guard before devirtualized call and
 // if it's true, starts to do deoptimization.
diff --git a/compiler/optimizing/optimizing_cfi_test_expected.inc b/compiler/optimizing/optimizing_cfi_test_expected.inc
index d84fe6c..60af2b4 100644
--- a/compiler/optimizing/optimizing_cfi_test_expected.inc
+++ b/compiler/optimizing/optimizing_cfi_test_expected.inc
@@ -174,53 +174,45 @@
 // 0x00000034: .cfi_def_cfa_offset: 64
 
 static constexpr uint8_t expected_asm_kMips64[] = {
-    0xD8, 0xFF, 0xBD, 0x67, 0x20, 0x00, 0xBF, 0xFF, 0x18, 0x00, 0xB1, 0xFF,
-    0x10, 0x00, 0xB0, 0xFF, 0x08, 0x00, 0xB9, 0xF7, 0x00, 0x00, 0xB8, 0xF7,
-    0xE8, 0xFF, 0xBD, 0x67, 0x18, 0x00, 0xBD, 0x67,
-    0x00, 0x00, 0xB8, 0xD7, 0x08, 0x00, 0xB9, 0xD7, 0x10, 0x00, 0xB0, 0xDF,
-    0x18, 0x00, 0xB1, 0xDF, 0x20, 0x00, 0xBF, 0xDF, 0x28, 0x00, 0xBD, 0x67,
-    0x09, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00,
+    0xC0, 0xFF, 0xBD, 0x67, 0x38, 0x00, 0xBF, 0xFF, 0x30, 0x00, 0xB1, 0xFF,
+    0x28, 0x00, 0xB0, 0xFF, 0x20, 0x00, 0xB9, 0xF7, 0x18, 0x00, 0xB8, 0xF7,
+    0x38, 0x00, 0xBF, 0xDF, 0x30, 0x00, 0xB1, 0xDF, 0x28, 0x00, 0xB0, 0xDF,
+    0x20, 0x00, 0xB9, 0xD7, 0x18, 0x00, 0xB8, 0xD7, 0x40, 0x00, 0xBD, 0x67,
+    0x00, 0x00, 0x1F, 0xD8,
 };
-
 static constexpr uint8_t expected_cfi_kMips64[] = {
-    0x44, 0x0E, 0x28, 0x44, 0x9F, 0x02, 0x44, 0x91, 0x04, 0x44, 0x90, 0x06,
-    0x44, 0xB9, 0x08, 0x44, 0xB8, 0x0A, 0x44, 0x0E, 0x40, 0x0A, 0x44,
-    0x0E, 0x28, 0x44, 0xF8, 0x44, 0xF9, 0x44, 0xD0, 0x44, 0xD1, 0x44, 0xDF,
-    0x44, 0x0E, 0x00, 0x48, 0x0B, 0x0E, 0x40,
+    0x44, 0x0E, 0x40, 0x44, 0x9F, 0x02, 0x44, 0x91, 0x04, 0x44, 0x90, 0x06,
+    0x44, 0xB9, 0x08, 0x44, 0xB8, 0x0A, 0x0A, 0x44, 0xDF, 0x44, 0xD1, 0x44,
+    0xD0, 0x44, 0xF9, 0x44, 0xF8, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0x40,
 };
-// 0x00000000: daddiu r29, r29, -40
-// 0x00000004: .cfi_def_cfa_offset: 40
-// 0x00000004: sd r31, +32(r29)
+// 0x00000000: daddiu r29, r29, -64
+// 0x00000004: .cfi_def_cfa_offset: 64
+// 0x00000004: sd r31, +56(r29)
 // 0x00000008: .cfi_offset: r31 at cfa-8
-// 0x00000008: sd r17, +24(r29)
+// 0x00000008: sd r17, +48(r29)
 // 0x0000000c: .cfi_offset: r17 at cfa-16
-// 0x0000000c: sd r16, +16(r29)
+// 0x0000000c: sd r16, +40(r29)
 // 0x00000010: .cfi_offset: r16 at cfa-24
-// 0x00000010: sdc1 f25, +8(r29)
+// 0x00000010: sdc1 f25, +32(r29)
 // 0x00000014: .cfi_offset: r57 at cfa-32
-// 0x00000014: sdc1 f24, +0(r29)
+// 0x00000014: sdc1 f24, +24(r29)
 // 0x00000018: .cfi_offset: r56 at cfa-40
-// 0x00000018: daddiu r29, r29, -24
-// 0x0000001c: .cfi_def_cfa_offset: 64
-// 0x0000001c: .cfi_remember_state
-// 0x0000001c: daddiu r29, r29, 24
-// 0x00000020: .cfi_def_cfa_offset: 40
-// 0x00000020: ldc1 f24, +0(r29)
-// 0x00000024: .cfi_restore: r56
-// 0x00000024: ldc1 f25, +8(r29)
+// 0x00000018: .cfi_remember_state
+// 0x00000018: ld r31, +56(r29)
+// 0x0000001c: .cfi_restore: r31
+// 0x0000001c: ld r17, +48(r29)
+// 0x00000020: .cfi_restore: r17
+// 0x00000020: ld r16, +40(r29)
+// 0x00000024: .cfi_restore: r16
+// 0x00000024: ldc1 f25, +32(r29)
 // 0x00000028: .cfi_restore: r57
-// 0x00000028: ld r16, +16(r29)
-// 0x0000002c: .cfi_restore: r16
-// 0x0000002c: ld r17, +24(r29)
-// 0x00000030: .cfi_restore: r17
-// 0x00000030: ld r31, +32(r29)
-// 0x00000034: .cfi_restore: r31
-// 0x00000034: daddiu r29, r29, 40
-// 0x00000038: .cfi_def_cfa_offset: 0
-// 0x00000038: jr r31
-// 0x0000003c: nop
-// 0x00000040: .cfi_restore_state
-// 0x00000040: .cfi_def_cfa_offset: 64
+// 0x00000028: ldc1 f24, +24(r29)
+// 0x0000002c: .cfi_restore: r56
+// 0x0000002c: daddiu r29, r29, 64
+// 0x00000030: .cfi_def_cfa_offset: 0
+// 0x00000030: jic r31, 0
+// 0x00000034: .cfi_restore_state
+// 0x00000034: .cfi_def_cfa_offset: 64
 
 static constexpr uint8_t expected_asm_kThumb2_adjust[] = {
 #ifdef ART_USE_OLD_ARM_BACKEND
@@ -403,58 +395,52 @@
 // 0x00020060: .cfi_def_cfa_offset: 64
 
 static constexpr uint8_t expected_asm_kMips64_adjust_head[] = {
-    0xD8, 0xFF, 0xBD, 0x67, 0x20, 0x00, 0xBF, 0xFF, 0x18, 0x00, 0xB1, 0xFF,
-    0x10, 0x00, 0xB0, 0xFF, 0x08, 0x00, 0xB9, 0xF7, 0x00, 0x00, 0xB8, 0xF7,
-    0xE8, 0xFF, 0xBD, 0x67, 0x02, 0x00, 0xA6, 0x60,
-    0x02, 0x00, 0x3E, 0xEC, 0x0C, 0x00, 0x01, 0xD8,
+    0xC0, 0xFF, 0xBD, 0x67, 0x38, 0x00, 0xBF, 0xFF, 0x30, 0x00, 0xB1, 0xFF,
+    0x28, 0x00, 0xB0, 0xFF, 0x20, 0x00, 0xB9, 0xF7, 0x18, 0x00, 0xB8, 0xF7,
+    0x02, 0x00, 0xA6, 0x60, 0x02, 0x00, 0x3E, 0xEC, 0x0C, 0x00, 0x01, 0xD8,
 };
 static constexpr uint8_t expected_asm_kMips64_adjust_tail[] = {
-    0x18, 0x00, 0xBD, 0x67, 0x00, 0x00, 0xB8, 0xD7, 0x08, 0x00, 0xB9, 0xD7,
-    0x10, 0x00, 0xB0, 0xDF, 0x18, 0x00, 0xB1, 0xDF, 0x20, 0x00, 0xBF, 0xDF,
-    0x28, 0x00, 0xBD, 0x67, 0x09, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00,
+    0x38, 0x00, 0xBF, 0xDF, 0x30, 0x00, 0xB1, 0xDF, 0x28, 0x00, 0xB0, 0xDF,
+    0x20, 0x00, 0xB9, 0xD7, 0x18, 0x00, 0xB8, 0xD7, 0x40, 0x00, 0xBD, 0x67,
+    0x00, 0x00, 0x1F, 0xD8,
 };
 static constexpr uint8_t expected_cfi_kMips64_adjust[] = {
-    0x44, 0x0E, 0x28, 0x44, 0x9F, 0x02, 0x44, 0x91, 0x04, 0x44, 0x90, 0x06,
-    0x44, 0xB9, 0x08, 0x44, 0xB8, 0x0A, 0x44, 0x0E, 0x40, 0x04, 0x10, 0x00,
-    0x02, 0x00, 0x0A, 0x44, 0x0E, 0x28, 0x44, 0xF8, 0x44, 0xF9, 0x44, 0xD0,
-    0x44, 0xD1, 0x44, 0xDF, 0x44, 0x0E, 0x00, 0x48, 0x0B, 0x0E, 0x40,
+    0x44, 0x0E, 0x40, 0x44, 0x9F, 0x02, 0x44, 0x91, 0x04, 0x44, 0x90, 0x06,
+    0x44, 0xB9, 0x08, 0x44, 0xB8, 0x0A, 0x04, 0x10, 0x00, 0x02, 0x00, 0x0A,
+    0x44, 0xDF, 0x44, 0xD1, 0x44, 0xD0, 0x44, 0xF9, 0x44, 0xF8, 0x44, 0x0E,
+    0x00, 0x44, 0x0B, 0x0E, 0x40,
 };
-// 0x00000000: daddiu r29, r29, -40
-// 0x00000004: .cfi_def_cfa_offset: 40
-// 0x00000004: sd r31, +32(r29)
+// 0x00000000: daddiu r29, r29, -64
+// 0x00000004: .cfi_def_cfa_offset: 64
+// 0x00000004: sd r31, +56(r29)
 // 0x00000008: .cfi_offset: r31 at cfa-8
-// 0x00000008: sd r17, +24(r29)
+// 0x00000008: sd r17, +48(r29)
 // 0x0000000c: .cfi_offset: r17 at cfa-16
-// 0x0000000c: sd r16, +16(r29)
+// 0x0000000c: sd r16, +40(r29)
 // 0x00000010: .cfi_offset: r16 at cfa-24
-// 0x00000010: sdc1 f25, +8(r29)
+// 0x00000010: sdc1 f25, +32(r29)
 // 0x00000014: .cfi_offset: r57 at cfa-32
-// 0x00000014: sdc1 f24, +0(r29)
+// 0x00000014: sdc1 f24, +24(r29)
 // 0x00000018: .cfi_offset: r56 at cfa-40
-// 0x00000018: daddiu r29, r29, -24
-// 0x0000001c: .cfi_def_cfa_offset: 64
-// 0x0000001c: bnec r5, r6, 0x0000002c ; +12
-// 0x00000020: auipc r1, 2
-// 0x00000024: jic r1, 12 ; b 0x00020030 ; +131080
-// 0x00000028: nop
+// 0x00000018: bnec r5, r6, 0x00000024 ; +12
+// 0x0000001c: auipc r1, 2
+// 0x00000020: jic r1, 12 ; bc 0x00020028 ; +131080
+// 0x00000024: nop
 //             ...
-// 0x00020028: nop
-// 0x0002002c: .cfi_remember_state
-// 0x0002002c: daddiu r29, r29, 24
-// 0x00020030: .cfi_def_cfa_offset: 40
-// 0x00020030: ldc1 f24, +0(r29)
-// 0x00020034: .cfi_restore: r56
-// 0x00020034: ldc1 f25, +8(r29)
+// 0x00020024: nop
+// 0x00020028: .cfi_remember_state
+// 0x00020028: ld r31, +56(r29)
+// 0x0002002c: .cfi_restore: r31
+// 0x0002002c: ld r17, +48(r29)
+// 0x00020030: .cfi_restore: r17
+// 0x00020030: ld r16, +40(r29)
+// 0x00020034: .cfi_restore: r16
+// 0x00020034: ldc1 f25, +32(r29)
 // 0x00020038: .cfi_restore: r57
-// 0x00020038: ld r16, +16(r29)
-// 0x0002003c: .cfi_restore: r16
-// 0x0002003c: ld r17, +24(r29)
-// 0x00020040: .cfi_restore: r17
-// 0x00020040: ld r31, +32(r29)
-// 0x00020044: .cfi_restore: r31
-// 0x00020044: daddiu r29, r29, 40
-// 0x00020047: .cfi_def_cfa_offset: 0
-// 0x00020048: jr r31
-// 0x0002004c: nop
-// 0x00020050: .cfi_restore_state
-// 0x00020050: .cfi_def_cfa_offset: 64
+// 0x00020038: ldc1 f24, +24(r29)
+// 0x0002003c: .cfi_restore: r56
+// 0x0002003c: daddiu r29, r29, 64
+// 0x00020040: .cfi_def_cfa_offset: 0
+// 0x00020040: jic r31, 0
+// 0x00020044: .cfi_restore_state
+// 0x00020044: .cfi_def_cfa_offset: 64
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 23ccd9e..3c6d2d6 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -499,7 +499,8 @@
                                 handles,
                                 stats,
                                 number_of_dex_registers,
-                                /* depth */ 0);
+                                /* total_number_of_instructions */ 0,
+                                /* parent */ nullptr);
   } else if (opt_name == HSharpening::kSharpeningPassName) {
     return new (arena) HSharpening(graph, codegen, dex_compilation_unit, driver, handles);
   } else if (opt_name == HSelectGenerator::kSelectGeneratorPassName) {
@@ -607,8 +608,7 @@
                                          VariableSizedHandleScope* handles) const {
   OptimizingCompilerStats* stats = compilation_stats_.get();
   const CompilerOptions& compiler_options = driver->GetCompilerOptions();
-  bool should_inline = (compiler_options.GetInlineDepthLimit() > 0)
-      && (compiler_options.GetInlineMaxCodeUnits() > 0);
+  bool should_inline = (compiler_options.GetInlineMaxCodeUnits() > 0);
   if (!should_inline) {
     return;
   }
@@ -623,7 +623,8 @@
       handles,
       stats,
       number_of_dex_registers,
-      /* depth */ 0);
+      /* total_number_of_instructions */ 0,
+      /* parent */ nullptr);
   HOptimization* optimizations[] = { inliner };
 
   RunOptimizations(optimizations, arraysize(optimizations), pass_observer);
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index ae9a811..a211c54 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -69,6 +69,23 @@
   kExplicitNullCheckGenerated,
   kSimplifyIf,
   kInstructionSunk,
+  kNotInlinedUnresolvedEntrypoint,
+  kNotInlinedDexCache,
+  kNotInlinedStackMaps,
+  kNotInlinedEnvironmentBudget,
+  kNotInlinedInstructionBudget,
+  kNotInlinedLoopWithoutExit,
+  kNotInlinedIrreducibleLoop,
+  kNotInlinedAlwaysThrows,
+  kNotInlinedInfiniteLoop,
+  kNotInlinedTryCatch,
+  kNotInlinedRegisterAllocator,
+  kNotInlinedCannotBuild,
+  kNotInlinedNotVerified,
+  kNotInlinedCodeItem,
+  kNotInlinedWont,
+  kNotInlinedRecursiveBudget,
+  kNotInlinedProxy,
   kLastStat
 };
 
@@ -168,6 +185,23 @@
       case kExplicitNullCheckGenerated: name = "ExplicitNullCheckGenerated"; break;
       case kSimplifyIf: name = "SimplifyIf"; break;
       case kInstructionSunk: name = "InstructionSunk"; break;
+      case kNotInlinedUnresolvedEntrypoint: name = "NotInlinedUnresolvedEntrypoint"; break;
+      case kNotInlinedDexCache: name = "NotInlinedDexCache"; break;
+      case kNotInlinedStackMaps: name = "NotInlinedStackMaps"; break;
+      case kNotInlinedEnvironmentBudget: name = "NotInlinedEnvironmentBudget"; break;
+      case kNotInlinedInstructionBudget: name = "NotInlinedInstructionBudget"; break;
+      case kNotInlinedLoopWithoutExit: name = "NotInlinedLoopWithoutExit"; break;
+      case kNotInlinedIrreducibleLoop: name = "NotInlinedIrreducibleLoop"; break;
+      case kNotInlinedAlwaysThrows: name = "NotInlinedAlwaysThrows"; break;
+      case kNotInlinedInfiniteLoop: name = "NotInlinedInfiniteLoop"; break;
+      case kNotInlinedTryCatch: name = "NotInlinedTryCatch"; break;
+      case kNotInlinedRegisterAllocator: name = "NotInlinedRegisterAllocator"; break;
+      case kNotInlinedCannotBuild: name = "NotInlinedCannotBuild"; break;
+      case kNotInlinedNotVerified: name = "NotInlinedNotVerified"; break;
+      case kNotInlinedCodeItem: name = "NotInlinedCodeItem"; break;
+      case kNotInlinedWont: name = "NotInlinedWont"; break;
+      case kNotInlinedRecursiveBudget: name = "NotInlinedRecursiveBudget"; break;
+      case kNotInlinedProxy: name = "NotInlinedProxy"; break;
 
       case kLastStat:
         LOG(FATAL) << "invalid stat "
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index efbaf6c..66bfea9 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -40,6 +40,14 @@
   check->ReplaceWith(check->InputAt(0));
 }
 
+void PrepareForRegisterAllocation::VisitDeoptimize(HDeoptimize* deoptimize) {
+  if (deoptimize->GuardsAnInput()) {
+    // Replace the uses with the actual guarded instruction.
+    deoptimize->ReplaceWith(deoptimize->GuardedInput());
+    deoptimize->RemoveGuard();
+  }
+}
+
 void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) {
   check->ReplaceWith(check->InputAt(0));
   if (check->IsStringCharAt()) {
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index c128227..7ffbe44 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -44,6 +44,7 @@
   void VisitClinitCheck(HClinitCheck* check) OVERRIDE;
   void VisitCondition(HCondition* condition) OVERRIDE;
   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
+  void VisitDeoptimize(HDeoptimize* deoptimize) OVERRIDE;
 
   bool CanMoveClinitCheck(HInstruction* input, HInstruction* user) const;
   bool CanEmitConditionAt(HCondition* condition, HInstruction* user) const;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 6e332ca..d5637b9 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -310,8 +310,8 @@
     BoundTypeIn(receiver, trueBlock, /* start_instruction */ nullptr, class_rti);
   } else {
     DCHECK(check->IsDeoptimize());
-    if (compare->IsEqual()) {
-      BoundTypeIn(receiver, check->GetBlock(), check, class_rti);
+    if (compare->IsEqual() && check->AsDeoptimize()->GuardsAnInput()) {
+      check->SetReferenceTypeInfo(class_rti);
     }
   }
 }
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index 84a4bab..0b49ce1 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -29,7 +29,7 @@
  */
 class ReferenceTypePropagationTest : public CommonCompilerTest {
  public:
-  ReferenceTypePropagationTest() : pool_(), allocator_(&pool_) {
+  ReferenceTypePropagationTest() : pool_(), allocator_(&pool_), propagation_(nullptr) {
     graph_ = CreateGraph(&allocator_);
   }
 
diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h
index ab0dad4..9236a0e 100644
--- a/compiler/optimizing/scheduler.h
+++ b/compiler/optimizing/scheduler.h
@@ -315,7 +315,10 @@
   // This class and its sub-classes will never be used to drive a visit of an
   // `HGraph` but only to visit `HInstructions` one at a time, so we do not need
   // to pass a valid graph to `HGraphDelegateVisitor()`.
-  SchedulingLatencyVisitor() : HGraphDelegateVisitor(nullptr) {}
+  SchedulingLatencyVisitor()
+      : HGraphDelegateVisitor(nullptr),
+        last_visited_latency_(0),
+        last_visited_internal_latency_(0) {}
 
   void VisitInstruction(HInstruction* instruction) OVERRIDE {
     LOG(FATAL) << "Error visiting " << instruction->DebugName() << ". "
@@ -413,6 +416,7 @@
         selector_(selector),
         only_optimize_loop_blocks_(true),
         scheduling_graph_(this, arena),
+        cursor_(nullptr),
         candidates_(arena_->Adapter(kArenaAllocScheduler)) {}
   virtual ~HScheduler() {}
 
diff --git a/compiler/optimizing/ssa_liveness_analysis_test.cc b/compiler/optimizing/ssa_liveness_analysis_test.cc
index 1916c73..a1016d1 100644
--- a/compiler/optimizing/ssa_liveness_analysis_test.cc
+++ b/compiler/optimizing/ssa_liveness_analysis_test.cc
@@ -189,13 +189,14 @@
   // Use HAboveOrEqual+HDeoptimize as the bounds check.
   HInstruction* ae = new (&allocator_) HAboveOrEqual(index, length);
   block->AddInstruction(ae);
-  HInstruction* deoptimize = new(&allocator_) HDeoptimize(ae, /* dex_pc */ 0u);
+  HInstruction* deoptimize =
+      new(&allocator_) HDeoptimize(&allocator_, ae, HDeoptimize::Kind::kBCE, /* dex_pc */ 0u);
   block->AddInstruction(deoptimize);
   HEnvironment* deoptimize_env = new (&allocator_) HEnvironment(&allocator_,
-                                                                  /* number_of_vregs */ 5,
-                                                                  /* method */ nullptr,
-                                                                  /* dex_pc */ 0u,
-                                                                  deoptimize);
+                                                                /* number_of_vregs */ 5,
+                                                                /* method */ nullptr,
+                                                                /* dex_pc */ 0u,
+                                                                deoptimize);
   deoptimize_env->CopyFrom(args);
   deoptimize->SetRawEnvironment(deoptimize_env);
   HInstruction* array_set =
diff --git a/compiler/utils/mips64/managed_register_mips64.cc b/compiler/utils/mips64/managed_register_mips64.cc
index dea396e..42d061e 100644
--- a/compiler/utils/mips64/managed_register_mips64.cc
+++ b/compiler/utils/mips64/managed_register_mips64.cc
@@ -26,6 +26,11 @@
   CHECK(IsValidManagedRegister());
   CHECK(other.IsValidManagedRegister());
   if (Equals(other)) return true;
+  if (IsFpuRegister() && other.IsVectorRegister()) {
+    return (AsFpuRegister() == other.AsOverlappingFpuRegister());
+  } else if (IsVectorRegister() && other.IsFpuRegister()) {
+    return (AsVectorRegister() == other.AsOverlappingVectorRegister());
+  }
   return false;
 }
 
@@ -36,6 +41,8 @@
     os << "GPU: " << static_cast<int>(AsGpuRegister());
   } else if (IsFpuRegister()) {
      os << "FpuRegister: " << static_cast<int>(AsFpuRegister());
+  } else if (IsVectorRegister()) {
+     os << "VectorRegister: " << static_cast<int>(AsVectorRegister());
   } else {
     os << "??: " << RegId();
   }
diff --git a/compiler/utils/mips64/managed_register_mips64.h b/compiler/utils/mips64/managed_register_mips64.h
index c9f9556..3980199 100644
--- a/compiler/utils/mips64/managed_register_mips64.h
+++ b/compiler/utils/mips64/managed_register_mips64.h
@@ -30,11 +30,27 @@
 const int kNumberOfFpuRegIds = kNumberOfFpuRegisters;
 const int kNumberOfFpuAllocIds = kNumberOfFpuRegisters;
 
-const int kNumberOfRegIds = kNumberOfGpuRegIds + kNumberOfFpuRegIds;
-const int kNumberOfAllocIds = kNumberOfGpuAllocIds + kNumberOfFpuAllocIds;
+const int kNumberOfVecRegIds = kNumberOfVectorRegisters;
+const int kNumberOfVecAllocIds = kNumberOfVectorRegisters;
 
-// An instance of class 'ManagedRegister' represents a single GPU register (enum
-// Register) or a double precision FP register (enum FpuRegister)
+const int kNumberOfRegIds = kNumberOfGpuRegIds + kNumberOfFpuRegIds + kNumberOfVecRegIds;
+const int kNumberOfAllocIds = kNumberOfGpuAllocIds + kNumberOfFpuAllocIds + kNumberOfVecAllocIds;
+
+// Register ids map:
+//   [0..R[  core registers (enum GpuRegister)
+//   [R..F[  floating-point registers (enum FpuRegister)
+//   [F..W[  MSA vector registers (enum VectorRegister)
+// where
+//   R = kNumberOfGpuRegIds
+//   F = R + kNumberOfFpuRegIds
+//   W = F + kNumberOfVecRegIds
+
+// An instance of class 'ManagedRegister' represents a single Mips64 register.
+// A register can be one of the following:
+//  * core register (enum GpuRegister)
+//  * floating-point register (enum FpuRegister)
+//  * MSA vector register (enum VectorRegister)
+//
 // 'ManagedRegister::NoRegister()' provides an invalid register.
 // There is a one-to-one mapping between ManagedRegister and register id.
 class Mips64ManagedRegister : public ManagedRegister {
@@ -49,6 +65,21 @@
     return static_cast<FpuRegister>(id_ - kNumberOfGpuRegIds);
   }
 
+  constexpr VectorRegister AsVectorRegister() const {
+    CHECK(IsVectorRegister());
+    return static_cast<VectorRegister>(id_ - (kNumberOfGpuRegIds + kNumberOfFpuRegisters));
+  }
+
+  constexpr FpuRegister AsOverlappingFpuRegister() const {
+    CHECK(IsValidManagedRegister());
+    return static_cast<FpuRegister>(AsVectorRegister());
+  }
+
+  constexpr VectorRegister AsOverlappingVectorRegister() const {
+    CHECK(IsValidManagedRegister());
+    return static_cast<VectorRegister>(AsFpuRegister());
+  }
+
   constexpr bool IsGpuRegister() const {
     CHECK(IsValidManagedRegister());
     return (0 <= id_) && (id_ < kNumberOfGpuRegIds);
@@ -60,6 +91,12 @@
     return (0 <= test) && (test < kNumberOfFpuRegIds);
   }
 
+  constexpr bool IsVectorRegister() const {
+    CHECK(IsValidManagedRegister());
+    const int test = id_ - (kNumberOfGpuRegIds + kNumberOfFpuRegIds);
+    return (0 <= test) && (test < kNumberOfVecRegIds);
+  }
+
   void Print(std::ostream& os) const;
 
   // Returns true if the two managed-registers ('this' and 'other') overlap.
@@ -77,6 +114,11 @@
     return FromRegId(r + kNumberOfGpuRegIds);
   }
 
+  static constexpr Mips64ManagedRegister FromVectorRegister(VectorRegister r) {
+    CHECK_NE(r, kNoVectorRegister);
+    return FromRegId(r + kNumberOfGpuRegIds + kNumberOfFpuRegIds);
+  }
+
  private:
   constexpr bool IsValidManagedRegister() const {
     return (0 <= id_) && (id_ < kNumberOfRegIds);
diff --git a/compiler/utils/mips64/managed_register_mips64_test.cc b/compiler/utils/mips64/managed_register_mips64_test.cc
new file mode 100644
index 0000000..8b72d7e
--- /dev/null
+++ b/compiler/utils/mips64/managed_register_mips64_test.cc
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "managed_register_mips64.h"
+#include "globals.h"
+#include "gtest/gtest.h"
+
+namespace art {
+namespace mips64 {
+
+TEST(Mips64ManagedRegister, NoRegister) {
+  Mips64ManagedRegister reg = ManagedRegister::NoRegister().AsMips64();
+  EXPECT_TRUE(reg.IsNoRegister());
+  EXPECT_FALSE(reg.Overlaps(reg));
+}
+
+TEST(Mips64ManagedRegister, GpuRegister) {
+  Mips64ManagedRegister reg = Mips64ManagedRegister::FromGpuRegister(ZERO);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(ZERO, reg.AsGpuRegister());
+
+  reg = Mips64ManagedRegister::FromGpuRegister(AT);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(AT, reg.AsGpuRegister());
+
+  reg = Mips64ManagedRegister::FromGpuRegister(V0);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(V0, reg.AsGpuRegister());
+
+  reg = Mips64ManagedRegister::FromGpuRegister(A0);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(A0, reg.AsGpuRegister());
+
+  reg = Mips64ManagedRegister::FromGpuRegister(A7);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(A7, reg.AsGpuRegister());
+
+  reg = Mips64ManagedRegister::FromGpuRegister(T0);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(T0, reg.AsGpuRegister());
+
+  reg = Mips64ManagedRegister::FromGpuRegister(T3);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(T3, reg.AsGpuRegister());
+
+  reg = Mips64ManagedRegister::FromGpuRegister(S0);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(S0, reg.AsGpuRegister());
+
+  reg = Mips64ManagedRegister::FromGpuRegister(GP);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(GP, reg.AsGpuRegister());
+
+  reg = Mips64ManagedRegister::FromGpuRegister(SP);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(SP, reg.AsGpuRegister());
+
+  reg = Mips64ManagedRegister::FromGpuRegister(RA);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_TRUE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_EQ(RA, reg.AsGpuRegister());
+}
+
+TEST(Mips64ManagedRegister, FpuRegister) {
+  Mips64ManagedRegister reg = Mips64ManagedRegister::FromFpuRegister(F0);
+  Mips64ManagedRegister vreg = Mips64ManagedRegister::FromVectorRegister(W0);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_FALSE(reg.IsGpuRegister());
+  EXPECT_TRUE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_TRUE(reg.Overlaps(vreg));
+  EXPECT_EQ(F0, reg.AsFpuRegister());
+  EXPECT_EQ(W0, reg.AsOverlappingVectorRegister());
+  EXPECT_TRUE(reg.Equals(Mips64ManagedRegister::FromFpuRegister(F0)));
+
+  reg = Mips64ManagedRegister::FromFpuRegister(F1);
+  vreg = Mips64ManagedRegister::FromVectorRegister(W1);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_FALSE(reg.IsGpuRegister());
+  EXPECT_TRUE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_TRUE(reg.Overlaps(vreg));
+  EXPECT_EQ(F1, reg.AsFpuRegister());
+  EXPECT_EQ(W1, reg.AsOverlappingVectorRegister());
+  EXPECT_TRUE(reg.Equals(Mips64ManagedRegister::FromFpuRegister(F1)));
+
+  reg = Mips64ManagedRegister::FromFpuRegister(F20);
+  vreg = Mips64ManagedRegister::FromVectorRegister(W20);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_FALSE(reg.IsGpuRegister());
+  EXPECT_TRUE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_TRUE(reg.Overlaps(vreg));
+  EXPECT_EQ(F20, reg.AsFpuRegister());
+  EXPECT_EQ(W20, reg.AsOverlappingVectorRegister());
+  EXPECT_TRUE(reg.Equals(Mips64ManagedRegister::FromFpuRegister(F20)));
+
+  reg = Mips64ManagedRegister::FromFpuRegister(F31);
+  vreg = Mips64ManagedRegister::FromVectorRegister(W31);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_FALSE(reg.IsGpuRegister());
+  EXPECT_TRUE(reg.IsFpuRegister());
+  EXPECT_FALSE(reg.IsVectorRegister());
+  EXPECT_TRUE(reg.Overlaps(vreg));
+  EXPECT_EQ(F31, reg.AsFpuRegister());
+  EXPECT_EQ(W31, reg.AsOverlappingVectorRegister());
+  EXPECT_TRUE(reg.Equals(Mips64ManagedRegister::FromFpuRegister(F31)));
+}
+
+TEST(Mips64ManagedRegister, VectorRegister) {
+  Mips64ManagedRegister reg = Mips64ManagedRegister::FromVectorRegister(W0);
+  Mips64ManagedRegister freg = Mips64ManagedRegister::FromFpuRegister(F0);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_FALSE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_TRUE(reg.IsVectorRegister());
+  EXPECT_TRUE(reg.Overlaps(freg));
+  EXPECT_EQ(W0, reg.AsVectorRegister());
+  EXPECT_EQ(F0, reg.AsOverlappingFpuRegister());
+  EXPECT_TRUE(reg.Equals(Mips64ManagedRegister::FromVectorRegister(W0)));
+
+  reg = Mips64ManagedRegister::FromVectorRegister(W2);
+  freg = Mips64ManagedRegister::FromFpuRegister(F2);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_FALSE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_TRUE(reg.IsVectorRegister());
+  EXPECT_TRUE(reg.Overlaps(freg));
+  EXPECT_EQ(W2, reg.AsVectorRegister());
+  EXPECT_EQ(F2, reg.AsOverlappingFpuRegister());
+  EXPECT_TRUE(reg.Equals(Mips64ManagedRegister::FromVectorRegister(W2)));
+
+  reg = Mips64ManagedRegister::FromVectorRegister(W13);
+  freg = Mips64ManagedRegister::FromFpuRegister(F13);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_FALSE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_TRUE(reg.IsVectorRegister());
+  EXPECT_TRUE(reg.Overlaps(freg));
+  EXPECT_EQ(W13, reg.AsVectorRegister());
+  EXPECT_EQ(F13, reg.AsOverlappingFpuRegister());
+  EXPECT_TRUE(reg.Equals(Mips64ManagedRegister::FromVectorRegister(W13)));
+
+  reg = Mips64ManagedRegister::FromVectorRegister(W29);
+  freg = Mips64ManagedRegister::FromFpuRegister(F29);
+  EXPECT_FALSE(reg.IsNoRegister());
+  EXPECT_FALSE(reg.IsGpuRegister());
+  EXPECT_FALSE(reg.IsFpuRegister());
+  EXPECT_TRUE(reg.IsVectorRegister());
+  EXPECT_TRUE(reg.Overlaps(freg));
+  EXPECT_EQ(W29, reg.AsVectorRegister());
+  EXPECT_EQ(F29, reg.AsOverlappingFpuRegister());
+  EXPECT_TRUE(reg.Equals(Mips64ManagedRegister::FromVectorRegister(W29)));
+}
+
+TEST(Mips64ManagedRegister, Equals) {
+  ManagedRegister no_reg = ManagedRegister::NoRegister();
+  EXPECT_TRUE(no_reg.Equals(Mips64ManagedRegister::NoRegister()));
+  EXPECT_FALSE(no_reg.Equals(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(no_reg.Equals(Mips64ManagedRegister::FromGpuRegister(A1)));
+  EXPECT_FALSE(no_reg.Equals(Mips64ManagedRegister::FromGpuRegister(S2)));
+  EXPECT_FALSE(no_reg.Equals(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(no_reg.Equals(Mips64ManagedRegister::FromVectorRegister(W0)));
+
+  Mips64ManagedRegister reg_ZERO = Mips64ManagedRegister::FromGpuRegister(ZERO);
+  EXPECT_FALSE(reg_ZERO.Equals(Mips64ManagedRegister::NoRegister()));
+  EXPECT_TRUE(reg_ZERO.Equals(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg_ZERO.Equals(Mips64ManagedRegister::FromGpuRegister(A1)));
+  EXPECT_FALSE(reg_ZERO.Equals(Mips64ManagedRegister::FromGpuRegister(S2)));
+  EXPECT_FALSE(reg_ZERO.Equals(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg_ZERO.Equals(Mips64ManagedRegister::FromVectorRegister(W0)));
+
+  Mips64ManagedRegister reg_A1 = Mips64ManagedRegister::FromGpuRegister(A1);
+  EXPECT_FALSE(reg_A1.Equals(Mips64ManagedRegister::NoRegister()));
+  EXPECT_FALSE(reg_A1.Equals(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg_A1.Equals(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_TRUE(reg_A1.Equals(Mips64ManagedRegister::FromGpuRegister(A1)));
+  EXPECT_FALSE(reg_A1.Equals(Mips64ManagedRegister::FromGpuRegister(S2)));
+  EXPECT_FALSE(reg_A1.Equals(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg_A1.Equals(Mips64ManagedRegister::FromVectorRegister(W0)));
+
+  Mips64ManagedRegister reg_S2 = Mips64ManagedRegister::FromGpuRegister(S2);
+  EXPECT_FALSE(reg_S2.Equals(Mips64ManagedRegister::NoRegister()));
+  EXPECT_FALSE(reg_S2.Equals(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg_S2.Equals(Mips64ManagedRegister::FromGpuRegister(A1)));
+  EXPECT_FALSE(reg_S2.Equals(Mips64ManagedRegister::FromGpuRegister(S1)));
+  EXPECT_TRUE(reg_S2.Equals(Mips64ManagedRegister::FromGpuRegister(S2)));
+  EXPECT_FALSE(reg_S2.Equals(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg_S2.Equals(Mips64ManagedRegister::FromVectorRegister(W0)));
+
+  Mips64ManagedRegister reg_F0 = Mips64ManagedRegister::FromFpuRegister(F0);
+  EXPECT_FALSE(reg_F0.Equals(Mips64ManagedRegister::NoRegister()));
+  EXPECT_FALSE(reg_F0.Equals(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg_F0.Equals(Mips64ManagedRegister::FromGpuRegister(A1)));
+  EXPECT_FALSE(reg_F0.Equals(Mips64ManagedRegister::FromGpuRegister(S2)));
+  EXPECT_TRUE(reg_F0.Equals(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg_F0.Equals(Mips64ManagedRegister::FromFpuRegister(F1)));
+  EXPECT_FALSE(reg_F0.Equals(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg_F0.Equals(Mips64ManagedRegister::FromVectorRegister(W0)));
+
+  Mips64ManagedRegister reg_F31 = Mips64ManagedRegister::FromFpuRegister(F31);
+  EXPECT_FALSE(reg_F31.Equals(Mips64ManagedRegister::NoRegister()));
+  EXPECT_FALSE(reg_F31.Equals(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg_F31.Equals(Mips64ManagedRegister::FromGpuRegister(A1)));
+  EXPECT_FALSE(reg_F31.Equals(Mips64ManagedRegister::FromGpuRegister(S2)));
+  EXPECT_FALSE(reg_F31.Equals(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg_F31.Equals(Mips64ManagedRegister::FromFpuRegister(F1)));
+  EXPECT_TRUE(reg_F31.Equals(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg_F31.Equals(Mips64ManagedRegister::FromVectorRegister(W0)));
+
+  Mips64ManagedRegister reg_W0 = Mips64ManagedRegister::FromVectorRegister(W0);
+  EXPECT_FALSE(reg_W0.Equals(Mips64ManagedRegister::NoRegister()));
+  EXPECT_FALSE(reg_W0.Equals(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg_W0.Equals(Mips64ManagedRegister::FromGpuRegister(A1)));
+  EXPECT_FALSE(reg_W0.Equals(Mips64ManagedRegister::FromGpuRegister(S1)));
+  EXPECT_FALSE(reg_W0.Equals(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_TRUE(reg_W0.Equals(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg_W0.Equals(Mips64ManagedRegister::FromVectorRegister(W1)));
+  EXPECT_FALSE(reg_W0.Equals(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  Mips64ManagedRegister reg_W31 = Mips64ManagedRegister::FromVectorRegister(W31);
+  EXPECT_FALSE(reg_W31.Equals(Mips64ManagedRegister::NoRegister()));
+  EXPECT_FALSE(reg_W31.Equals(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg_W31.Equals(Mips64ManagedRegister::FromGpuRegister(A1)));
+  EXPECT_FALSE(reg_W31.Equals(Mips64ManagedRegister::FromGpuRegister(S1)));
+  EXPECT_FALSE(reg_W31.Equals(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg_W31.Equals(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg_W31.Equals(Mips64ManagedRegister::FromVectorRegister(W1)));
+  EXPECT_TRUE(reg_W31.Equals(Mips64ManagedRegister::FromVectorRegister(W31)));
+}
+
+TEST(Mips64ManagedRegister, Overlaps) {
+  Mips64ManagedRegister reg = Mips64ManagedRegister::FromFpuRegister(F0);
+  Mips64ManagedRegister reg_o = Mips64ManagedRegister::FromVectorRegister(W0);
+  EXPECT_TRUE(reg.Overlaps(reg_o));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_EQ(F0, reg_o.AsOverlappingFpuRegister());
+  EXPECT_EQ(W0, reg.AsOverlappingVectorRegister());
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromFpuRegister(F4);
+  reg_o = Mips64ManagedRegister::FromVectorRegister(W4);
+  EXPECT_TRUE(reg.Overlaps(reg_o));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_EQ(F4, reg_o.AsOverlappingFpuRegister());
+  EXPECT_EQ(W4, reg.AsOverlappingVectorRegister());
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromFpuRegister(F16);
+  reg_o = Mips64ManagedRegister::FromVectorRegister(W16);
+  EXPECT_TRUE(reg.Overlaps(reg_o));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_EQ(F16, reg_o.AsOverlappingFpuRegister());
+  EXPECT_EQ(W16, reg.AsOverlappingVectorRegister());
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromFpuRegister(F31);
+  reg_o = Mips64ManagedRegister::FromVectorRegister(W31);
+  EXPECT_TRUE(reg.Overlaps(reg_o));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_EQ(F31, reg_o.AsOverlappingFpuRegister());
+  EXPECT_EQ(W31, reg.AsOverlappingVectorRegister());
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromVectorRegister(W0);
+  reg_o = Mips64ManagedRegister::FromFpuRegister(F0);
+  EXPECT_TRUE(reg.Overlaps(reg_o));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_EQ(W0, reg_o.AsOverlappingVectorRegister());
+  EXPECT_EQ(F0, reg.AsOverlappingFpuRegister());
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromVectorRegister(W4);
+  reg_o = Mips64ManagedRegister::FromFpuRegister(F4);
+  EXPECT_TRUE(reg.Overlaps(reg_o));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_EQ(W4, reg_o.AsOverlappingVectorRegister());
+  EXPECT_EQ(F4, reg.AsOverlappingFpuRegister());
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromVectorRegister(W16);
+  reg_o = Mips64ManagedRegister::FromFpuRegister(F16);
+  EXPECT_TRUE(reg.Overlaps(reg_o));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_EQ(W16, reg_o.AsOverlappingVectorRegister());
+  EXPECT_EQ(F16, reg.AsOverlappingFpuRegister());
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromVectorRegister(W31);
+  reg_o = Mips64ManagedRegister::FromFpuRegister(F31);
+  EXPECT_TRUE(reg.Overlaps(reg_o));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_EQ(W31, reg_o.AsOverlappingVectorRegister());
+  EXPECT_EQ(F31, reg.AsOverlappingFpuRegister());
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromGpuRegister(ZERO);
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromGpuRegister(A0);
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromGpuRegister(S0);
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+
+  reg = Mips64ManagedRegister::FromGpuRegister(RA);
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(ZERO)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(A0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(S0)));
+  EXPECT_TRUE(reg.Overlaps(Mips64ManagedRegister::FromGpuRegister(RA)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromFpuRegister(F31)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W0)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W4)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W16)));
+  EXPECT_FALSE(reg.Overlaps(Mips64ManagedRegister::FromVectorRegister(W31)));
+}
+
+}  // namespace mips64
+}  // namespace art
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 4bfc849..fa7e985 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -18,21 +18,21 @@
 #include "verifier/verifier_deps.h"
 
 #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 "common_compiler_test.h"
 #include "compiler_callbacks.h"
+#include "dex/verification_results.h"
+#include "dex/verified_method.h"
 #include "dex_file.h"
 #include "dex_file_types.h"
+#include "driver/compiler_options.h"
+#include "driver/compiler_driver.h"
 #include "handle_scope-inl.h"
 #include "verifier/method_verifier-inl.h"
 #include "mirror/class_loader.h"
 #include "runtime.h"
 #include "thread.h"
 #include "scoped_thread_state_change-inl.h"
+#include "utils/atomic_method_ref_map-inl.h"
 
 namespace art {
 namespace verifier {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index e80be81..b4ea20b 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -312,13 +312,6 @@
   UsageError("      Example: --num-dex-method=%d", CompilerOptions::kDefaultNumDexMethodsThreshold);
   UsageError("      Default: %d", CompilerOptions::kDefaultNumDexMethodsThreshold);
   UsageError("");
-  UsageError("  --inline-depth-limit=<depth-limit>: the depth limit of inlining for fine tuning");
-  UsageError("      the compiler. A zero value will disable inlining. Honored only by Optimizing.");
-  UsageError("      Has priority over the --compiler-filter option. Intended for ");
-  UsageError("      development/experimental use.");
-  UsageError("      Example: --inline-depth-limit=%d", CompilerOptions::kDefaultInlineDepthLimit);
-  UsageError("      Default: %d", CompilerOptions::kDefaultInlineDepthLimit);
-  UsageError("");
   UsageError("  --inline-max-code-units=<code-units-count>: the maximum code units that a method");
   UsageError("      can have to be considered for inlining. A zero value will disable inlining.");
   UsageError("      Honored only by Optimizing. Has priority over the --compiler-filter option.");
@@ -870,22 +863,8 @@
       }
     }
 
-    // It they are not set, use default values for inlining settings.
-    // TODO: We should rethink the compiler filter. We mostly save
-    // time here, which is orthogonal to space.
-    if (compiler_options_->inline_depth_limit_ == CompilerOptions::kUnsetInlineDepthLimit) {
-      compiler_options_->inline_depth_limit_ =
-          (compiler_options_->compiler_filter_ == CompilerFilter::kSpace)
-          // Implementation of the space filter: limit inlining depth.
-          ? CompilerOptions::kSpaceFilterInlineDepthLimit
-          : CompilerOptions::kDefaultInlineDepthLimit;
-    }
     if (compiler_options_->inline_max_code_units_ == CompilerOptions::kUnsetInlineMaxCodeUnits) {
-      compiler_options_->inline_max_code_units_ =
-          (compiler_options_->compiler_filter_ == CompilerFilter::kSpace)
-          // Implementation of the space filter: limit inlining max code units.
-          ? CompilerOptions::kSpaceFilterInlineMaxCodeUnits
-          : CompilerOptions::kDefaultInlineMaxCodeUnits;
+      compiler_options_->inline_max_code_units_ = CompilerOptions::kDefaultInlineMaxCodeUnits;
     }
 
     // Checks are all explicit until we know the architecture.
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 615bcf9..105610e 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1700,7 +1700,7 @@
   std::unique_ptr<File> new_file;
   if (!options_.output_to_memmap_) {
     std::string output_location(options_.output_dex_directory_);
-    size_t last_slash = dex_file_location.rfind("/");
+    size_t last_slash = dex_file_location.rfind('/');
     std::string dex_file_directory = dex_file_location.substr(0, last_slash + 1);
     if (output_location == dex_file_directory) {
       output_location = dex_file_location + ".new";
diff --git a/profman/profman.cc b/profman/profman.cc
index fdb9a75..dac95b8 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -418,7 +418,7 @@
     return true;
   }
 
-  bool GetClassNames(std::string profile_file,
+  bool GetClassNames(const std::string& profile_file,
                      std::vector<std::unique_ptr<const DexFile>>* dex_files,
                      std::set<std::string>* class_names) {
     int fd = open(profile_file.c_str(), O_RDONLY);
@@ -711,7 +711,7 @@
     }
     std::vector<ProfileMethodInfo::ProfileClassReference> classes(inline_cache_elems.size());
     size_t class_it = 0;
-    for (const std::string ic_class : inline_cache_elems) {
+    for (const std::string& ic_class : inline_cache_elems) {
       if (!FindClass(dex_files, ic_class, &(classes[class_it++]))) {
         LOG(ERROR) << "Could not find class: " << ic_class;
         return false;
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 2248c3b..8f09cc6 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -691,7 +691,7 @@
 
     // Pointer to JNI function registered to this method, or a function to resolve the JNI function,
     // or the profiling data for non-native methods, or an ImtConflictTable, or the
-    // single-implementation of an abstract method.
+    // single-implementation of an abstract/interface method.
     void* data_;
 
     // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 5aede38..e763e43 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -166,7 +166,7 @@
   MEMORY_TOOL_MAKE_NOACCESS(ptr, size);
 }
 
-Arena::Arena() : bytes_allocated_(0), next_(nullptr) {
+Arena::Arena() : bytes_allocated_(0), memory_(nullptr), size_(0), next_(nullptr) {
 }
 
 class MallocArena FINAL : public Arena {
diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h
index ca9a694..b28eb72 100644
--- a/runtime/base/histogram-inl.h
+++ b/runtime/base/histogram-inl.h
@@ -48,7 +48,8 @@
     : kAdjust(0),
       kInitialBucketCount(0),
       name_(name),
-      max_buckets_(0) {
+      max_buckets_(0),
+      sample_size_(0) {
 }
 
 template <class Value>
diff --git a/runtime/cha.cc b/runtime/cha.cc
index eaba01b..7948c29 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -210,7 +210,7 @@
   }
 }
 
-void ClassHierarchyAnalysis::CheckSingleImplementationInfo(
+void ClassHierarchyAnalysis::CheckVirtualMethodSingleImplementationInfo(
     Handle<mirror::Class> klass,
     ArtMethod* virtual_method,
     ArtMethod* method_in_super,
@@ -290,8 +290,9 @@
       // A non-abstract method overrides an abstract method.
       if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) {
         // Abstract method_in_super has no implementation yet.
-        // We need to grab cha_lock_ for further checking/updating due to possible
-        // races.
+        // We need to grab cha_lock_ since there may be multiple class linking
+        // going on that can check/modify the single-implementation flag/method
+        // of method_in_super.
         MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_);
         if (!method_in_super->HasSingleImplementation()) {
           return;
@@ -362,6 +363,55 @@
   }
 }
 
+void ClassHierarchyAnalysis::CheckInterfaceMethodSingleImplementationInfo(
+    Handle<mirror::Class> klass,
+    ArtMethod* interface_method,
+    ArtMethod* implementation_method,
+    std::unordered_set<ArtMethod*>& invalidated_single_impl_methods,
+    PointerSize pointer_size) {
+  DCHECK(klass->IsInstantiable());
+  DCHECK(interface_method->IsAbstract() || interface_method->IsDefault());
+
+  if (!interface_method->HasSingleImplementation()) {
+    return;
+  }
+
+  if (implementation_method->IsAbstract()) {
+    // An instantiable class doesn't supply an implementation for
+    // interface_method. Invoking the interface method on the class will throw
+    // AbstractMethodError. This is an uncommon case, so we simply treat
+    // interface_method as not having single-implementation.
+    invalidated_single_impl_methods.insert(interface_method);
+    return;
+  }
+
+  // We need to grab cha_lock_ since there may be multiple class linking going
+  // on that can check/modify the single-implementation flag/method of
+  // interface_method.
+  MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_);
+  // Do this check again after we grab cha_lock_.
+  if (!interface_method->HasSingleImplementation()) {
+    return;
+  }
+
+  ArtMethod* single_impl = interface_method->GetSingleImplementation(pointer_size);
+  if (single_impl == nullptr) {
+    // implementation_method becomes the first implementation for
+    // interface_method.
+    interface_method->SetSingleImplementation(implementation_method, pointer_size);
+    // Keep interface_method's single-implementation status.
+    return;
+  }
+  DCHECK(!single_impl->IsAbstract());
+  if (single_impl->GetDeclaringClass() == implementation_method->GetDeclaringClass()) {
+    // Same implementation. Since implementation_method may be a copy of a default
+    // method, we need to check the declaring class for equality.
+    return;
+  }
+  // Another implementation for interface_method.
+  invalidated_single_impl_methods.insert(interface_method);
+}
+
 void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> klass,
                                                           ArtMethod* method,
                                                           PointerSize pointer_size) {
@@ -382,6 +432,7 @@
       // Rare case, but we do accept it (such as 800-smali/smali/b_26143249.smali).
       // Do not attempt to devirtualize it.
       method->SetHasSingleImplementation(false);
+      DCHECK(method->GetSingleImplementation(pointer_size) == nullptr);
     } else {
       // Abstract method starts with single-implementation flag set and null
       // implementation method.
@@ -396,9 +447,15 @@
 }
 
 void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle<mirror::Class> klass) {
+  PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   if (klass->IsInterface()) {
+    for (ArtMethod& method : klass->GetDeclaredVirtualMethods(image_pointer_size)) {
+      DCHECK(method.IsAbstract() || method.IsDefault());
+      InitSingleImplementationFlag(klass, &method, image_pointer_size);
+    }
     return;
   }
+
   mirror::Class* super_class = klass->GetSuperClass();
   if (super_class == nullptr) {
     return;
@@ -408,7 +465,6 @@
   // is invalidated by linking `klass`.
   std::unordered_set<ArtMethod*> invalidated_single_impl_methods;
 
-  PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   // Do an entry-by-entry comparison of vtable contents with super's vtable.
   for (int32_t i = 0; i < super_class->GetVTableLength(); ++i) {
     ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size);
@@ -418,33 +474,59 @@
       if (method->IsAbstract() && klass->IsInstantiable()) {
         // An instantiable class that inherits an abstract method is treated as
         // supplying an implementation that throws AbstractMethodError.
-        CheckSingleImplementationInfo(klass,
-                                      method,
-                                      method_in_super,
-                                      invalidated_single_impl_methods,
-                                      image_pointer_size);
+        CheckVirtualMethodSingleImplementationInfo(klass,
+                                                   method,
+                                                   method_in_super,
+                                                   invalidated_single_impl_methods,
+                                                   image_pointer_size);
       }
       continue;
     }
     InitSingleImplementationFlag(klass, method, image_pointer_size);
-    CheckSingleImplementationInfo(klass,
-                                  method,
-                                  method_in_super,
-                                  invalidated_single_impl_methods,
-                                  image_pointer_size);
+    CheckVirtualMethodSingleImplementationInfo(klass,
+                                               method,
+                                               method_in_super,
+                                               invalidated_single_impl_methods,
+                                               image_pointer_size);
   }
-
   // For new virtual methods that don't override.
   for (int32_t i = super_class->GetVTableLength(); i < klass->GetVTableLength(); ++i) {
     ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size);
     InitSingleImplementationFlag(klass, method, image_pointer_size);
   }
 
-  Runtime* const runtime = Runtime::Current();
+  if (klass->IsInstantiable()) {
+    auto* iftable = klass->GetIfTable();
+    const size_t ifcount = klass->GetIfTableCount();
+    for (size_t i = 0; i < ifcount; ++i) {
+      mirror::Class* interface = iftable->GetInterface(i);
+      for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) {
+        ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size);
+        mirror::PointerArray* method_array = iftable->GetMethodArray(i);
+        ArtMethod* implementation_method =
+            method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size);
+        DCHECK(implementation_method != nullptr) << klass->PrettyClass();
+        CheckInterfaceMethodSingleImplementationInfo(klass,
+                                                     interface_method,
+                                                     implementation_method,
+                                                     invalidated_single_impl_methods,
+                                                     image_pointer_size);
+      }
+    }
+  }
+
+  InvalidateSingleImplementationMethods(invalidated_single_impl_methods);
+}
+
+void ClassHierarchyAnalysis::InvalidateSingleImplementationMethods(
+    std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) {
   if (!invalidated_single_impl_methods.empty()) {
+    Runtime* const runtime = Runtime::Current();
     Thread *self = Thread::Current();
     // Method headers for compiled code to be invalidated.
     std::unordered_set<OatQuickMethodHeader*> dependent_method_headers;
+    PointerSize image_pointer_size =
+        Runtime::Current()->GetClassLinker()->GetImagePointerSize();
 
     {
       // We do this under cha_lock_. Committing code also grabs this lock to
diff --git a/runtime/cha.h b/runtime/cha.h
index a56a752..99c49d2 100644
--- a/runtime/cha.h
+++ b/runtime/cha.h
@@ -117,11 +117,13 @@
                                     PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Check/update single-implementation info when one virtual method
+  // overrides another.
   // `virtual_method` in `klass` overrides `method_in_super`.
-  // This will invalidate some assumptions on single-implementation.
+  // This may invalidate some assumptions on single-implementation.
   // Append methods that should have their single-implementation flag invalidated
   // to `invalidated_single_impl_methods`.
-  void CheckSingleImplementationInfo(
+  void CheckVirtualMethodSingleImplementationInfo(
       Handle<mirror::Class> klass,
       ArtMethod* virtual_method,
       ArtMethod* method_in_super,
@@ -129,6 +131,23 @@
       PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Check/update single-implementation info when one method
+  // implements an interface method.
+  // `implementation_method` in `klass` implements `interface_method`.
+  // Append `interface_method` to `invalidated_single_impl_methods`
+  // if `interface_method` gets a new implementation.
+  void CheckInterfaceMethodSingleImplementationInfo(
+      Handle<mirror::Class> klass,
+      ArtMethod* interface_method,
+      ArtMethod* implementation_method,
+      std::unordered_set<ArtMethod*>& invalidated_single_impl_methods,
+      PointerSize pointer_size)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  void InvalidateSingleImplementationMethods(
+      std::unordered_set<ArtMethod*>& invalidated_single_impl_methods)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // For all methods in vtable slot at `verify_index` of `verify_class` and its
   // superclasses, single-implementation status should be false, except if the
   // method is `excluded_method`.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 8162a82..f936db9 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3505,13 +3505,12 @@
     return dex_cache;
   }
   // Failure, dump diagnostic and abort.
-  std::string location(dex_file.GetLocation());
   for (const DexCacheData& data : dex_caches_) {
     if (DecodeDexCache(self, data) != nullptr) {
-      LOG(ERROR) << "Registered dex file " << data.dex_file->GetLocation();
+      LOG(FATAL_WITHOUT_ABORT) << "Registered dex file " << data.dex_file->GetLocation();
     }
   }
-  LOG(FATAL) << "Failed to find DexCache for DexFile " << location;
+  LOG(FATAL) << "Failed to find DexCache for DexFile " << dex_file.GetLocation();
   UNREACHABLE();
 }
 
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 9f04e59..b421810 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -618,7 +618,7 @@
   ClassExtOffsets() : CheckOffsets<mirror::ClassExt>(false, "Ldalvik/system/ClassExt;") {
     addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_dex_caches_), "obsoleteDexCaches");
     addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_methods_), "obsoleteMethods");
-    addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_bytes_), "originalDexFile");
+    addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_), "originalDexFile");
     addOffset(OFFSETOF_MEMBER(mirror::ClassExt, verify_error_), "verifyError");
   }
 };
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 64128cc..4220250 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -38,8 +38,8 @@
 }
 
 // Signal handler called on SIGSEGV.
-static void art_fault_handler(int sig, siginfo_t* info, void* context) {
-  fault_manager.HandleFault(sig, info, context);
+static bool art_fault_handler(int sig, siginfo_t* info, void* context) {
+  return fault_manager.HandleFault(sig, info, context);
 }
 
 FaultManager::FaultManager() : initialized_(false) {
@@ -49,43 +49,15 @@
 FaultManager::~FaultManager() {
 }
 
-static void SetUpArtAction(struct sigaction* action) {
-  action->sa_sigaction = art_fault_handler;
-  sigemptyset(&action->sa_mask);
-  action->sa_flags = SA_SIGINFO | SA_ONSTACK;
-#if !defined(__APPLE__) && !defined(__mips__)
-  action->sa_restorer = nullptr;
-#endif
-}
-
-void FaultManager::EnsureArtActionInFrontOfSignalChain() {
-  if (initialized_) {
-    struct sigaction action;
-    SetUpArtAction(&action);
-    EnsureFrontOfChain(SIGSEGV, &action);
-  } else {
-    LOG(WARNING) << "Can't call " << __FUNCTION__ << " due to unitialized fault manager";
-  }
-}
-
 void FaultManager::Init() {
   CHECK(!initialized_);
-  struct sigaction action;
-  SetUpArtAction(&action);
-
-  // Set our signal handler now.
-  int e = sigaction(SIGSEGV, &action, &oldaction_);
-  if (e != 0) {
-    VLOG(signals) << "Failed to claim SEGV: " << strerror(errno);
-  }
-  // Make sure our signal handler is called before any user handlers.
-  ClaimSignalChain(SIGSEGV, &oldaction_);
+  AddSpecialSignalHandlerFn(SIGSEGV, art_fault_handler);
   initialized_ = true;
 }
 
 void FaultManager::Release() {
   if (initialized_) {
-    UnclaimSignalChain(SIGSEGV);
+    RemoveSpecialSignalHandlerFn(SIGSEGV, art_fault_handler);
     initialized_ = false;
   }
 }
@@ -118,93 +90,36 @@
   return false;
 }
 
-class ScopedSignalUnblocker {
- public:
-  explicit ScopedSignalUnblocker(const std::initializer_list<int>& signals) {
-    sigset_t new_mask;
-    sigemptyset(&new_mask);
-    for (int signal : signals) {
-      sigaddset(&new_mask, signal);
-    }
-    if (sigprocmask(SIG_UNBLOCK, &new_mask, &previous_mask_) != 0) {
-      PLOG(FATAL) << "failed to unblock signals";
-    }
-  }
-
-  ~ScopedSignalUnblocker() {
-    if (sigprocmask(SIG_SETMASK, &previous_mask_, nullptr) != 0) {
-      PLOG(FATAL) << "failed to unblock signals";
-    }
-  }
-
- private:
-  sigset_t previous_mask_;
-};
-
-class ScopedHandlingSignalSetter {
- public:
-  explicit ScopedHandlingSignalSetter(Thread* thread) : thread_(thread) {
-    CHECK(!thread->HandlingSignal());
-    thread_->SetHandlingSignal(true);
-  }
-
-  ~ScopedHandlingSignalSetter() {
-    CHECK(thread_->HandlingSignal());
-    thread_->SetHandlingSignal(false);
-  }
-
- private:
-  Thread* thread_;
-};
-
-void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
-  // BE CAREFUL ALLOCATING HERE INCLUDING USING LOG(...)
-  //
-  // If malloc calls abort, it will be holding its lock.
-  // If the handler tries to call malloc, it will deadlock.
-
-  // Use a thread local field to track whether we're recursing, and fall back.
-  // (e.g.. if one of our handlers crashed)
-  Thread* thread = Thread::Current();
-
-  if (thread != nullptr && !thread->HandlingSignal()) {
-    // Unblock some signals and set thread->handling_signal_ to true,
-    // so that we can catch crashes in our signal handler.
-    ScopedHandlingSignalSetter setter(thread);
-    ScopedSignalUnblocker unblocker { SIGABRT, SIGBUS, SIGSEGV }; // NOLINT
-
-    VLOG(signals) << "Handling fault";
+bool FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
+  VLOG(signals) << "Handling fault";
 
 #ifdef TEST_NESTED_SIGNAL
-    // Simulate a crash in a handler.
-    raise(SIGSEGV);
+  // Simulate a crash in a handler.
+  raise(SIGSEGV);
 #endif
 
-    if (IsInGeneratedCode(info, context, true)) {
-      VLOG(signals) << "in generated code, looking for handler";
-      for (const auto& handler : generated_code_handlers_) {
-        VLOG(signals) << "invoking Action on handler " << handler;
-        if (handler->Action(sig, info, context)) {
-          // We have handled a signal so it's time to return from the
-          // signal handler to the appropriate place.
-          return;
-        }
+  if (IsInGeneratedCode(info, context, true)) {
+    VLOG(signals) << "in generated code, looking for handler";
+    for (const auto& handler : generated_code_handlers_) {
+      VLOG(signals) << "invoking Action on handler " << handler;
+      if (handler->Action(sig, info, context)) {
+        // We have handled a signal so it's time to return from the
+        // signal handler to the appropriate place.
+        return true;
       }
+    }
 
-      // We hit a signal we didn't handle.  This might be something for which
-      // we can give more information about so call all registered handlers to
-      // see if it is.
-      if (HandleFaultByOtherHandlers(sig, info, context)) {
-        return;
-      }
+    // We hit a signal we didn't handle.  This might be something for which
+    // we can give more information about so call all registered handlers to
+    // see if it is.
+    if (HandleFaultByOtherHandlers(sig, info, context)) {
+      return true;
     }
   }
 
   // Set a breakpoint in this function to catch unhandled signals.
   art_sigsegv_fault();
-
-  // Pass this on to the next handler in the chain, or the default if none.
-  InvokeUserSignalHandler(sig, info, context);
+  return false;
 }
 
 void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) {
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index ce59ba7..d56cf17 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -42,9 +42,9 @@
 
   // Unclaim signals and delete registered handlers.
   void Shutdown();
-  void EnsureArtActionInFrontOfSignalChain();
 
-  void HandleFault(int sig, siginfo_t* info, void* context);
+  // Try to handle a fault, returns true if successful.
+  bool HandleFault(int sig, siginfo_t* info, void* context);
 
   // Added handlers are owned by the fault handler and will be freed on Shutdown().
   void AddHandler(FaultHandler* handler, bool generated_code);
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 1fa2d1a..562fc75 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -141,7 +141,7 @@
   template<bool kUseTail = true>
   class SlotFreeList {
    public:
-    SlotFreeList() : head_(0U), tail_(0), size_(0) {}
+    SlotFreeList() : head_(0U), tail_(0), size_(0), padding_(0) {}
     Slot* Head() const {
       return reinterpret_cast<Slot*>(head_);
     }
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index d2ab41d..24ba52f 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -72,12 +72,19 @@
       rb_mark_bit_stack_full_(false),
       mark_stack_lock_("concurrent copying mark stack lock", kMarkSweepMarkStackLock),
       thread_running_gc_(nullptr),
-      is_marking_(false), is_active_(false), is_asserting_to_space_invariant_(false),
+      is_marking_(false),
+      is_active_(false),
+      is_asserting_to_space_invariant_(false),
       region_space_bitmap_(nullptr),
-      heap_mark_bitmap_(nullptr), live_stack_freeze_size_(0), mark_stack_mode_(kMarkStackModeOff),
+      heap_mark_bitmap_(nullptr),
+      live_stack_freeze_size_(0),
+      from_space_num_objects_at_first_pause_(0),
+      from_space_num_bytes_at_first_pause_(0),
+      mark_stack_mode_(kMarkStackModeOff),
       weak_ref_access_enabled_(true),
       skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock),
       measure_read_barrier_slow_path_(measure_read_barrier_slow_path),
+      mark_from_read_barrier_measurements_(false),
       rb_slow_path_ns_(0),
       rb_slow_path_count_(0),
       rb_slow_path_count_gc_(0),
@@ -87,6 +94,7 @@
       rb_slow_path_count_gc_total_(0),
       rb_table_(heap_->GetReadBarrierTable()),
       force_evacuate_all_(false),
+      gc_grays_immune_objects_(false),
       immune_gray_stack_lock_("concurrent copying immune gray stack lock",
                               kMarkSweepMarkStackLock) {
   static_assert(space::RegionSpace::kRegionSize == accounting::ReadBarrierTable::kRegionSize,
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index 0039388..c61f69d 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -52,8 +52,12 @@
 
 MarkCompact::MarkCompact(Heap* heap, const std::string& name_prefix)
     : GarbageCollector(heap, name_prefix + (name_prefix.empty() ? "" : " ") + "mark compact"),
+      mark_stack_(nullptr),
       space_(nullptr),
+      mark_bitmap_(nullptr),
       collector_name_(name_),
+      bump_pointer_(nullptr),
+      live_objects_in_space_(0),
       updating_references_(false) {}
 
 void MarkCompact::RunPhases() {
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 4c6b5bf..3988073 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -16,13 +16,12 @@
 
 #include "large_object_space.h"
 
-#include <valgrind.h>
 #include <memory>
-#include <memcheck/memcheck.h>
 
 #include "gc/accounting/heap_bitmap-inl.h"
 #include "gc/accounting/space_bitmap-inl.h"
 #include "base/logging.h"
+#include "base/memory_tool.h"
 #include "base/mutex-inl.h"
 #include "base/stl_util.h"
 #include "image.h"
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 1b3d339..67e949f 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -232,12 +232,6 @@
   kSwitchImplKind,        // Switch-based interpreter implementation.
   kMterpImplKind          // Assembly interpreter
 };
-static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs) {
-  os << ((rhs == kSwitchImplKind)
-              ? "Switch-based interpreter"
-              : "Asm interpreter");
-  return os;
-}
 
 static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind;
 
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 0ae7307..869d430 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
-#include "interpreter/interpreter_common.h"
 #include "interpreter/interpreter_intrinsics.h"
 
+#include "compiler/intrinsics_enum.h"
+#include "dex_instruction.h"
+#include "interpreter/interpreter_common.h"
+
 namespace art {
 namespace interpreter {
 
diff --git a/runtime/interpreter/interpreter_intrinsics.h b/runtime/interpreter/interpreter_intrinsics.h
index ae45679..2a23002 100644
--- a/runtime/interpreter/interpreter_intrinsics.h
+++ b/runtime/interpreter/interpreter_intrinsics.h
@@ -17,10 +17,14 @@
 #ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
 #define ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
 
-#include "compiler/intrinsics_enum.h"
-#include "dex_instruction.h"
+#include "jvalue.h"
 
 namespace art {
+
+class ArtMethod;
+class Instruction;
+class ShadowFrame;
+
 namespace interpreter {
 
 // Invokes to methods identified as intrinics are routed here.  If there is
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index a341cdb..b93b8f2 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -39,6 +39,7 @@
 #include "runtime_options.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change-inl.h"
+#include "sigchain.h"
 #include "thread-inl.h"
 #include "thread_list.h"
 
@@ -900,7 +901,8 @@
     int version = (*jni_on_load)(this, nullptr);
 
     if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
-      fault_manager.EnsureArtActionInFrontOfSignalChain();
+      // Make sure that sigchain owns SIGSEGV.
+      EnsureFrontOfChain(SIGSEGV);
     }
 
     self->SetClassLoaderOverride(old_class_loader.get());
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 5da1ea1..4f5bebf 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -279,6 +279,10 @@
         code_cache_initial_capacity_(0),
         code_cache_max_capacity_(0),
         compile_threshold_(0),
+        warmup_threshold_(0),
+        osr_threshold_(0),
+        priority_thread_weight_(0),
+        invoke_transition_weight_(0),
         dump_info_on_shutdown_(false) {}
 
   DISALLOW_COPY_AND_ASSIGN(JitOptions);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index fc41f94..e9a5ae5 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1366,7 +1366,10 @@
   MutexLock mu(self, lock_);
   ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize);
   if (info != nullptr) {
-    info->IncrementInlineUse();
+    if (!info->IncrementInlineUse()) {
+      // Overflow of inlining uses, just bail.
+      return nullptr;
+    }
   }
   return info;
 }
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index b23a863..a2c459c 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -498,13 +498,13 @@
   return true;
 }
 
-#define READ_UINT(type, buffer, dest, error)          \
-  do {                                                \
-    if (!buffer.ReadUintAndAdvance<type>(&dest)) {    \
-      *error = "Could not read "#dest;                \
-      return false;                                   \
-    }                                                 \
-  }                                                   \
+#define READ_UINT(type, buffer, dest, error)            \
+  do {                                                  \
+    if (!(buffer).ReadUintAndAdvance<type>(&(dest))) {  \
+      *(error) = "Could not read "#dest;                \
+      return false;                                     \
+    }                                                   \
+  }                                                     \
   while (false)
 
 bool ProfileCompilationInfo::ReadInlineCache(SafeBuffer& buffer,
@@ -1027,7 +1027,7 @@
       }
     }
     os << "\n\tmethods: ";
-    for (const auto method_it : dex_data.method_map) {
+    for (const auto& method_it : dex_data.method_map) {
       if (dex_file != nullptr) {
         os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
       } else {
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 6ad528c..451d53e 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -86,7 +86,7 @@
 
   // A dex location together with its checksum.
   struct DexReference {
-    DexReference() {}
+    DexReference() : dex_checksum(0) {}
 
     DexReference(const std::string& location, uint32_t checksum)
         : dex_location(location), dex_checksum(checksum) {}
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index f42a8da..d6881aa 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -108,9 +108,15 @@
     }
   }
 
-  void IncrementInlineUse() {
-    DCHECK_NE(current_inline_uses_, std::numeric_limits<uint16_t>::max());
+  // Increments the number of times this method is currently being inlined.
+  // Returns whether it was successful, that is it could increment without
+  // overflowing.
+  bool IncrementInlineUse() {
+    if (current_inline_uses_ == std::numeric_limits<uint16_t>::max()) {
+      return false;
+    }
     current_inline_uses_++;
+    return true;
   }
 
   void DecrementInlineUse() {
diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc
index 5dc3aca..94e4b88 100644
--- a/runtime/mirror/class_ext.cc
+++ b/runtime/mirror/class_ext.cc
@@ -117,9 +117,9 @@
   }
 }
 
-void ClassExt::SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) {
+void ClassExt::SetOriginalDexFile(ObjPtr<Object> bytes) {
   DCHECK(!Runtime::Current()->IsActiveTransaction());
-  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_), bytes);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_), bytes);
 }
 
 void ClassExt::SetClass(ObjPtr<Class> dalvik_system_ClassExt) {
diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h
index fac955a..708665d 100644
--- a/runtime/mirror/class_ext.h
+++ b/runtime/mirror/class_ext.h
@@ -60,11 +60,11 @@
         OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_));
   }
 
-  ByteArray* GetOriginalDexFileBytes() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetFieldObject<ByteArray>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_));
+  Object* GetOriginalDexFile() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldObject<Object>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_));
   }
 
-  void SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) REQUIRES_SHARED(Locks::mutator_lock_);
+  void SetOriginalDexFile(ObjPtr<Object> bytes) REQUIRES_SHARED(Locks::mutator_lock_);
 
   void SetObsoleteArrays(ObjPtr<PointerArray> methods, ObjPtr<ObjectArray<DexCache>> dex_caches)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -89,7 +89,7 @@
 
   HeapReference<PointerArray> obsolete_methods_;
 
-  HeapReference<ByteArray> original_dex_file_bytes_;
+  HeapReference<Object> original_dex_file_;
 
   // The saved verification error of this class.
   HeapReference<Object> verify_error_;
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 78b2e15..cf570b8 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -65,7 +65,7 @@
   DexCachePair(ObjPtr<T> object, uint32_t index)
       : object(object),
         index(index) {}
-  DexCachePair() = default;
+  DexCachePair() : index(0) {}
   DexCachePair(const DexCachePair<T>&) = default;
   DexCachePair& operator=(const DexCachePair<T>&) = default;
 
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 1fa4682..e80d31c 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -354,7 +354,7 @@
 // For use only by the JDWP implementation.
 class MonitorInfo {
  public:
-  MonitorInfo() = default;
+  MonitorInfo() : owner_(nullptr), entry_count_(0) {}
   MonitorInfo(const MonitorInfo&) = default;
   MonitorInfo& operator=(const MonitorInfo&) = default;
   explicit MonitorInfo(mirror::Object* o) REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index c58854b..d77cfa1 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -118,7 +118,7 @@
       for (int signal = 0; signal < _NSIG; ++signal) {
         android::NativeBridgeSignalHandlerFn fn = android::NativeBridgeGetSignalHandler(signal);
         if (fn != nullptr) {
-          SetSpecialSignalHandlerFn(signal, fn);
+          AddSpecialSignalHandlerFn(signal, fn);
         }
       }
 #endif
diff --git a/runtime/nth_caller_visitor.h b/runtime/nth_caller_visitor.h
index f72a853..71c6a82 100644
--- a/runtime/nth_caller_visitor.h
+++ b/runtime/nth_caller_visitor.h
@@ -31,7 +31,8 @@
         n(n_in),
         include_runtime_and_upcalls_(include_runtime_and_upcalls),
         count(0),
-        caller(nullptr) {}
+        caller(nullptr),
+        caller_pc(0) {}
 
   bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
     ArtMethod* m = GetMethod();
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 3396ce0..db6f8ee 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -640,15 +640,8 @@
   std::string dir = location.substr(0, pos+1);
   dir += "oat/" + std::string(GetInstructionSetString(isa));
 
-  // Find the file portion of the dex location.
-  std::string file;
-  if (pos == std::string::npos) {
-    file = location;
-  } else {
-    file = location.substr(pos+1);
-  }
-
   // Get the base part of the file without the extension.
+  std::string file = location.substr(pos+1);
   pos = file.rfind('.');
   if (pos == std::string::npos) {
     *error_msg = "Dex location " + location + " has no extension.";
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index d61e994..b84e711 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -369,7 +369,7 @@
     std::unique_ptr<OatFile> file_;
 
     bool status_attempted_ = false;
-    OatStatus status_;
+    OatStatus status_ = OatStatus::kOatCannotOpen;
 
     // For debugging only.
     // If this flag is set, the file has been released to the user and the
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 5401e5c..448e1ed 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -79,20 +79,26 @@
 
 class JvmtiFunctions {
  private:
-  static bool IsValidEnv(jvmtiEnv* env) {
-    return env != nullptr;
+  static jvmtiError getEnvironmentError(jvmtiEnv* env) {
+    if (env == nullptr) {
+      return ERR(INVALID_ENVIRONMENT);
+    } else if (art::Thread::Current() == nullptr) {
+      return ERR(UNATTACHED_THREAD);
+    } else {
+      return OK;
+    }
   }
 
-#define ENSURE_VALID_ENV(env)          \
-  do {                                 \
-    if (!IsValidEnv(env)) {            \
-      return ERR(INVALID_ENVIRONMENT); \
-    }                                  \
+#define ENSURE_VALID_ENV(env)                                            \
+  do {                                                                   \
+    jvmtiError ensure_valid_env_ ## __LINE__ = getEnvironmentError(env); \
+    if (ensure_valid_env_ ## __LINE__ != OK) {                           \
+      return ensure_valid_env_ ## __LINE__ ;                             \
+    }                                                                    \
   } while (false)
 
 #define ENSURE_HAS_CAP(env, cap) \
   do { \
-    ENSURE_VALID_ENV(env); \
     if (ArtJvmTiEnv::AsArtJvmTiEnv(env)->capabilities.cap != 1) { \
       return ERR(MUST_POSSESS_CAPABILITY); \
     } \
@@ -121,18 +127,22 @@
   }
 
   static jvmtiError GetThreadState(jvmtiEnv* env, jthread thread, jint* thread_state_ptr) {
+    ENSURE_VALID_ENV(env);
     return ThreadUtil::GetThreadState(env, thread, thread_state_ptr);
   }
 
   static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr) {
+    ENSURE_VALID_ENV(env);
     return ThreadUtil::GetCurrentThread(env, thread_ptr);
   }
 
   static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr) {
+    ENSURE_VALID_ENV(env);
     return ThreadUtil::GetAllThreads(env, threads_count_ptr, threads_ptr);
   }
 
   static jvmtiError SuspendThread(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_suspend);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -141,11 +151,13 @@
                                       jint request_count ATTRIBUTE_UNUSED,
                                       const jthread* request_list ATTRIBUTE_UNUSED,
                                       jvmtiError* results ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_suspend);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError ResumeThread(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_suspend);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -154,6 +166,7 @@
                                      jint request_count ATTRIBUTE_UNUSED,
                                      const jthread* request_list ATTRIBUTE_UNUSED,
                                      jvmtiError* results ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_suspend);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -161,16 +174,19 @@
   static jvmtiError StopThread(jvmtiEnv* env,
                                jthread thread ATTRIBUTE_UNUSED,
                                jobject exception ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_signal_thread);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError InterruptThread(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_signal_thread);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr) {
+    ENSURE_VALID_ENV(env);
     return ThreadUtil::GetThreadInfo(env, thread, info_ptr);
   }
 
@@ -178,6 +194,7 @@
                                         jthread thread ATTRIBUTE_UNUSED,
                                         jint* owned_monitor_count_ptr ATTRIBUTE_UNUSED,
                                         jobject** owned_monitors_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_owned_monitor_info);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -187,6 +204,7 @@
       jthread thread ATTRIBUTE_UNUSED,
       jint* monitor_info_count_ptr ATTRIBUTE_UNUSED,
       jvmtiMonitorStackDepthInfo** monitor_info_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_owned_monitor_stack_depth_info);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -194,6 +212,7 @@
   static jvmtiError GetCurrentContendedMonitor(jvmtiEnv* env,
                                                jthread thread ATTRIBUTE_UNUSED,
                                                jobject* monitor_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_current_contended_monitor);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -203,26 +222,31 @@
                                    jvmtiStartFunction proc,
                                    const void* arg,
                                    jint priority) {
+    ENSURE_VALID_ENV(env);
     return ThreadUtil::RunAgentThread(env, thread, proc, arg, priority);
   }
 
   static jvmtiError SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data) {
+    ENSURE_VALID_ENV(env);
     return ThreadUtil::SetThreadLocalStorage(env, thread, data);
   }
 
   static jvmtiError GetThreadLocalStorage(jvmtiEnv* env, jthread thread, void** data_ptr) {
+    ENSURE_VALID_ENV(env);
     return ThreadUtil::GetThreadLocalStorage(env, thread, data_ptr);
   }
 
   static jvmtiError GetTopThreadGroups(jvmtiEnv* env,
                                        jint* group_count_ptr,
                                        jthreadGroup** groups_ptr) {
+    ENSURE_VALID_ENV(env);
     return ThreadGroupUtil::GetTopThreadGroups(env, group_count_ptr, groups_ptr);
   }
 
   static jvmtiError GetThreadGroupInfo(jvmtiEnv* env,
                                        jthreadGroup group,
                                        jvmtiThreadGroupInfo* info_ptr) {
+    ENSURE_VALID_ENV(env);
     return ThreadGroupUtil::GetThreadGroupInfo(env, group, info_ptr);
   }
 
@@ -232,6 +256,7 @@
                                            jthread** threads_ptr,
                                            jint* group_count_ptr,
                                            jthreadGroup** groups_ptr) {
+    ENSURE_VALID_ENV(env);
     return ThreadGroupUtil::GetThreadGroupChildren(env,
                                                    group,
                                                    thread_count_ptr,
@@ -246,6 +271,7 @@
                                   jint max_frame_count,
                                   jvmtiFrameInfo* frame_buffer,
                                   jint* count_ptr) {
+    ENSURE_VALID_ENV(env);
     return StackUtil::GetStackTrace(env,
                                     thread,
                                     start_depth,
@@ -258,6 +284,7 @@
                                       jint max_frame_count,
                                       jvmtiStackInfo** stack_info_ptr,
                                       jint* thread_count_ptr) {
+    ENSURE_VALID_ENV(env);
     return StackUtil::GetAllStackTraces(env, max_frame_count, stack_info_ptr, thread_count_ptr);
   }
 
@@ -266,6 +293,7 @@
                                              const jthread* thread_list,
                                              jint max_frame_count,
                                              jvmtiStackInfo** stack_info_ptr) {
+    ENSURE_VALID_ENV(env);
     return StackUtil::GetThreadListStackTraces(env,
                                                thread_count,
                                                thread_list,
@@ -274,10 +302,12 @@
   }
 
   static jvmtiError GetFrameCount(jvmtiEnv* env, jthread thread, jint* count_ptr) {
+    ENSURE_VALID_ENV(env);
     return StackUtil::GetFrameCount(env, thread, count_ptr);
   }
 
   static jvmtiError PopFrame(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_pop_frame);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -287,12 +317,14 @@
                                      jint depth,
                                      jmethodID* method_ptr,
                                      jlocation* location_ptr) {
+    ENSURE_VALID_ENV(env);
     return StackUtil::GetFrameLocation(env, thread, depth, method_ptr, location_ptr);
   }
 
   static jvmtiError NotifyFramePop(jvmtiEnv* env,
                                    jthread thread ATTRIBUTE_UNUSED,
                                    jint depth ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_generate_frame_pop_events);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -300,6 +332,7 @@
   static jvmtiError ForceEarlyReturnObject(jvmtiEnv* env,
                                            jthread thread ATTRIBUTE_UNUSED,
                                            jobject value ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_force_early_return);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -307,6 +340,7 @@
   static jvmtiError ForceEarlyReturnInt(jvmtiEnv* env,
                                         jthread thread ATTRIBUTE_UNUSED,
                                         jint value ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_force_early_return);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -314,6 +348,7 @@
   static jvmtiError ForceEarlyReturnLong(jvmtiEnv* env,
                                          jthread thread ATTRIBUTE_UNUSED,
                                          jlong value ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_force_early_return);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -321,6 +356,7 @@
   static jvmtiError ForceEarlyReturnFloat(jvmtiEnv* env,
                                           jthread thread ATTRIBUTE_UNUSED,
                                           jfloat value ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_force_early_return);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -328,11 +364,13 @@
   static jvmtiError ForceEarlyReturnDouble(jvmtiEnv* env,
                                            jthread thread ATTRIBUTE_UNUSED,
                                            jdouble value ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_force_early_return);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError ForceEarlyReturnVoid(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_force_early_return);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -343,6 +381,7 @@
                                      jobject initial_object,
                                      const jvmtiHeapCallbacks* callbacks,
                                      const void* user_data) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_tag_objects);
     HeapUtil heap_util(ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get());
     return heap_util.FollowReferences(env,
@@ -358,12 +397,14 @@
                                        jclass klass,
                                        const jvmtiHeapCallbacks* callbacks,
                                        const void* user_data) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_tag_objects);
     HeapUtil heap_util(ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get());
     return heap_util.IterateThroughHeap(env, heap_filter, klass, callbacks, user_data);
   }
 
   static jvmtiError GetTag(jvmtiEnv* env, jobject object, jlong* tag_ptr) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_tag_objects);
 
     JNIEnv* jni_env = GetJniEnv(env);
@@ -381,6 +422,7 @@
   }
 
   static jvmtiError SetTag(jvmtiEnv* env, jobject object, jlong tag) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_tag_objects);
 
     if (object == nullptr) {
@@ -405,6 +447,7 @@
                                        jint* count_ptr,
                                        jobject** object_result_ptr,
                                        jlong** tag_result_ptr) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_tag_objects);
 
     JNIEnv* jni_env = GetJniEnv(env);
@@ -422,6 +465,7 @@
   }
 
   static jvmtiError ForceGarbageCollection(jvmtiEnv* env) {
+    ENSURE_VALID_ENV(env);
     return HeapUtil::ForceGarbageCollection(env);
   }
 
@@ -430,6 +474,7 @@
       jobject object ATTRIBUTE_UNUSED,
       jvmtiObjectReferenceCallback object_reference_callback ATTRIBUTE_UNUSED,
       const void* user_data ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_tag_objects);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -440,6 +485,7 @@
       jvmtiStackReferenceCallback stack_ref_callback ATTRIBUTE_UNUSED,
       jvmtiObjectReferenceCallback object_ref_callback ATTRIBUTE_UNUSED,
       const void* user_data ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_tag_objects);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -448,6 +494,7 @@
                                     jvmtiHeapObjectFilter object_filter ATTRIBUTE_UNUSED,
                                     jvmtiHeapObjectCallback heap_object_callback ATTRIBUTE_UNUSED,
                                     const void* user_data ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_tag_objects);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -458,6 +505,7 @@
       jvmtiHeapObjectFilter object_filter ATTRIBUTE_UNUSED,
       jvmtiHeapObjectCallback heap_object_callback ATTRIBUTE_UNUSED,
       const void* user_data ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_tag_objects);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -467,6 +515,7 @@
                                    jint depth ATTRIBUTE_UNUSED,
                                    jint slot ATTRIBUTE_UNUSED,
                                    jobject* value_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -475,6 +524,7 @@
                                      jthread thread ATTRIBUTE_UNUSED,
                                      jint depth ATTRIBUTE_UNUSED,
                                      jobject* value_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -484,6 +534,7 @@
                                 jint depth ATTRIBUTE_UNUSED,
                                 jint slot ATTRIBUTE_UNUSED,
                                 jint* value_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -493,6 +544,7 @@
                                  jint depth ATTRIBUTE_UNUSED,
                                  jint slot ATTRIBUTE_UNUSED,
                                  jlong* value_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -502,6 +554,7 @@
                                   jint depth ATTRIBUTE_UNUSED,
                                   jint slot ATTRIBUTE_UNUSED,
                                   jfloat* value_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -511,6 +564,7 @@
                                    jint depth ATTRIBUTE_UNUSED,
                                    jint slot ATTRIBUTE_UNUSED,
                                    jdouble* value_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -520,6 +574,7 @@
                                    jint depth ATTRIBUTE_UNUSED,
                                    jint slot ATTRIBUTE_UNUSED,
                                    jobject value ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -529,6 +584,7 @@
                                 jint depth ATTRIBUTE_UNUSED,
                                 jint slot ATTRIBUTE_UNUSED,
                                 jint value ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -538,6 +594,7 @@
                                  jint depth ATTRIBUTE_UNUSED,
                                  jint slot ATTRIBUTE_UNUSED,
                                  jlong value ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -547,6 +604,7 @@
                                   jint depth ATTRIBUTE_UNUSED,
                                   jint slot ATTRIBUTE_UNUSED,
                                   jfloat value ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -556,6 +614,7 @@
                                    jint depth ATTRIBUTE_UNUSED,
                                    jint slot ATTRIBUTE_UNUSED,
                                    jdouble value ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -563,6 +622,7 @@
   static jvmtiError SetBreakpoint(jvmtiEnv* env,
                                   jmethodID method ATTRIBUTE_UNUSED,
                                   jlocation location ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -570,6 +630,7 @@
   static jvmtiError ClearBreakpoint(jvmtiEnv* env,
                                     jmethodID method ATTRIBUTE_UNUSED,
                                     jlocation location ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -577,6 +638,7 @@
   static jvmtiError SetFieldAccessWatch(jvmtiEnv* env,
                                         jclass klass ATTRIBUTE_UNUSED,
                                         jfieldID field ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_generate_field_access_events);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -584,6 +646,7 @@
   static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env,
                                           jclass klass ATTRIBUTE_UNUSED,
                                           jfieldID field ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_generate_field_access_events);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -591,6 +654,7 @@
   static jvmtiError SetFieldModificationWatch(jvmtiEnv* env,
                                               jclass klass ATTRIBUTE_UNUSED,
                                               jfieldID field ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_generate_field_modification_events);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -598,11 +662,13 @@
   static jvmtiError ClearFieldModificationWatch(jvmtiEnv* env,
                                                 jclass klass ATTRIBUTE_UNUSED,
                                                 jfieldID field ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_generate_field_modification_events);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError GetLoadedClasses(jvmtiEnv* env, jint* class_count_ptr, jclass** classes_ptr) {
+    ENSURE_VALID_ENV(env);
     HeapUtil heap_util(ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get());
     return heap_util.GetLoadedClasses(env, class_count_ptr, classes_ptr);
   }
@@ -611,6 +677,7 @@
                                           jobject initiating_loader,
                                           jint* class_count_ptr,
                                           jclass** classes_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::GetClassLoaderClasses(env, initiating_loader, class_count_ptr, classes_ptr);
   }
 
@@ -618,21 +685,25 @@
                                       jclass klass,
                                       char** signature_ptr,
                                       char** generic_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::GetClassSignature(env, klass, signature_ptr, generic_ptr);
   }
 
   static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::GetClassStatus(env, klass, status_ptr);
   }
 
   static jvmtiError GetSourceFileName(jvmtiEnv* env,
                                       jclass klass ATTRIBUTE_UNUSED,
                                       char** source_name_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_source_file_name);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::GetClassModifiers(env, klass, modifiers_ptr);
   }
 
@@ -640,6 +711,7 @@
                                     jclass klass,
                                     jint* method_count_ptr,
                                     jmethodID** methods_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::GetClassMethods(env, klass, method_count_ptr, methods_ptr);
   }
 
@@ -647,6 +719,7 @@
                                    jclass klass,
                                    jint* field_count_ptr,
                                    jfieldID** fields_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::GetClassFields(env, klass, field_count_ptr, fields_ptr);
   }
 
@@ -654,6 +727,7 @@
                                              jclass klass,
                                              jint* interface_count_ptr,
                                              jclass** interfaces_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::GetImplementedInterfaces(env, klass, interface_count_ptr, interfaces_ptr);
   }
 
@@ -661,6 +735,7 @@
                                            jclass klass,
                                            jint* minor_version_ptr,
                                            jint* major_version_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::GetClassVersionNumbers(env, klass, minor_version_ptr, major_version_ptr);
   }
 
@@ -669,38 +744,45 @@
                                     jint* constant_pool_count_ptr ATTRIBUTE_UNUSED,
                                     jint* constant_pool_byte_count_ptr ATTRIBUTE_UNUSED,
                                     unsigned char** constant_pool_bytes_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_constant_pool);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::IsInterface(env, klass, is_interface_ptr);
   }
 
   static jvmtiError IsArrayClass(jvmtiEnv* env,
                                  jclass klass,
                                  jboolean* is_array_class_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::IsArrayClass(env, klass, is_array_class_ptr);
   }
 
   static jvmtiError IsModifiableClass(jvmtiEnv* env,
                                       jclass klass,
                                       jboolean* is_modifiable_class_ptr) {
+    ENSURE_VALID_ENV(env);
     return Redefiner::IsModifiableClass(env, klass, is_modifiable_class_ptr);
   }
 
   static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr) {
+    ENSURE_VALID_ENV(env);
     return ClassUtil::GetClassLoader(env, klass, classloader_ptr);
   }
 
   static jvmtiError GetSourceDebugExtension(jvmtiEnv* env,
                                             jclass klass ATTRIBUTE_UNUSED,
                                             char** source_debug_extension_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_source_debug_extension);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError RetransformClasses(jvmtiEnv* env, jint class_count, const jclass* classes) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_retransform_classes);
     std::string error_msg;
     jvmtiError res = Transformer::RetransformClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env),
@@ -719,6 +801,7 @@
   static jvmtiError RedefineClasses(jvmtiEnv* env,
                                     jint class_count,
                                     const jvmtiClassDefinition* class_definitions) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_redefine_classes);
     std::string error_msg;
     jvmtiError res = Redefiner::RedefineClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env),
@@ -735,16 +818,19 @@
   }
 
   static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr) {
+    ENSURE_VALID_ENV(env);
     return ObjectUtil::GetObjectSize(env, object, size_ptr);
   }
 
   static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr) {
+    ENSURE_VALID_ENV(env);
     return ObjectUtil::GetObjectHashCode(env, object, hash_code_ptr);
   }
 
   static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env,
                                           jobject object ATTRIBUTE_UNUSED,
                                           jvmtiMonitorUsage* info_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_monitor_info);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -755,6 +841,7 @@
                                  char** name_ptr,
                                  char** signature_ptr,
                                  char** generic_ptr) {
+    ENSURE_VALID_ENV(env);
     return FieldUtil::GetFieldName(env, klass, field, name_ptr, signature_ptr, generic_ptr);
   }
 
@@ -762,6 +849,7 @@
                                            jclass klass,
                                            jfieldID field,
                                            jclass* declaring_class_ptr) {
+    ENSURE_VALID_ENV(env);
     return FieldUtil::GetFieldDeclaringClass(env, klass, field, declaring_class_ptr);
   }
 
@@ -769,6 +857,7 @@
                                       jclass klass,
                                       jfieldID field,
                                       jint* modifiers_ptr) {
+    ENSURE_VALID_ENV(env);
     return FieldUtil::GetFieldModifiers(env, klass, field, modifiers_ptr);
   }
 
@@ -776,6 +865,7 @@
                                      jclass klass,
                                      jfieldID field,
                                      jboolean* is_synthetic_ptr) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_synthetic_attribute);
     return FieldUtil::IsFieldSynthetic(env, klass, field, is_synthetic_ptr);
   }
@@ -785,30 +875,35 @@
                                   char** name_ptr,
                                   char** signature_ptr,
                                   char** generic_ptr) {
+    ENSURE_VALID_ENV(env);
     return MethodUtil::GetMethodName(env, method, name_ptr, signature_ptr, generic_ptr);
   }
 
   static jvmtiError GetMethodDeclaringClass(jvmtiEnv* env,
                                             jmethodID method,
                                             jclass* declaring_class_ptr) {
+    ENSURE_VALID_ENV(env);
     return MethodUtil::GetMethodDeclaringClass(env, method, declaring_class_ptr);
   }
 
   static jvmtiError GetMethodModifiers(jvmtiEnv* env,
                                        jmethodID method,
                                        jint* modifiers_ptr) {
+    ENSURE_VALID_ENV(env);
     return MethodUtil::GetMethodModifiers(env, method, modifiers_ptr);
   }
 
   static jvmtiError GetMaxLocals(jvmtiEnv* env,
                                  jmethodID method,
                                  jint* max_ptr) {
+    ENSURE_VALID_ENV(env);
     return MethodUtil::GetMaxLocals(env, method, max_ptr);
   }
 
   static jvmtiError GetArgumentsSize(jvmtiEnv* env,
                                      jmethodID method,
                                      jint* size_ptr) {
+    ENSURE_VALID_ENV(env);
     return MethodUtil::GetArgumentsSize(env, method, size_ptr);
   }
 
@@ -816,6 +911,7 @@
                                        jmethodID method,
                                        jint* entry_count_ptr,
                                        jvmtiLineNumberEntry** table_ptr) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_line_numbers);
     return MethodUtil::GetLineNumberTable(env, method, entry_count_ptr, table_ptr);
   }
@@ -824,6 +920,7 @@
                                       jmethodID method,
                                       jlocation* start_location_ptr,
                                       jlocation* end_location_ptr) {
+    ENSURE_VALID_ENV(env);
     return MethodUtil::GetMethodLocation(env, method, start_location_ptr, end_location_ptr);
   }
 
@@ -831,6 +928,7 @@
                                           jmethodID method ATTRIBUTE_UNUSED,
                                           jint* entry_count_ptr ATTRIBUTE_UNUSED,
                                           jvmtiLocalVariableEntry** table_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_access_local_variables);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -839,24 +937,29 @@
                                  jmethodID method ATTRIBUTE_UNUSED,
                                  jint* bytecode_count_ptr ATTRIBUTE_UNUSED,
                                  unsigned char** bytecodes_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_bytecodes);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) {
+    ENSURE_VALID_ENV(env);
     return MethodUtil::IsMethodNative(env, method, is_native_ptr);
   }
 
   static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_synthetic_attribute);
     return MethodUtil::IsMethodSynthetic(env, method, is_synthetic_ptr);
   }
 
   static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr) {
+    ENSURE_VALID_ENV(env);
     return MethodUtil::IsMethodObsolete(env, method, is_obsolete_ptr);
   }
 
   static jvmtiError SetNativeMethodPrefix(jvmtiEnv* env, const char* prefix ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_set_native_method_prefix);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -864,43 +967,53 @@
   static jvmtiError SetNativeMethodPrefixes(jvmtiEnv* env,
                                             jint prefix_count ATTRIBUTE_UNUSED,
                                             char** prefixes ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_set_native_method_prefix);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError CreateRawMonitor(jvmtiEnv* env, const char* name, jrawMonitorID* monitor_ptr) {
+    ENSURE_VALID_ENV(env);
     return MonitorUtil::CreateRawMonitor(env, name, monitor_ptr);
   }
 
   static jvmtiError DestroyRawMonitor(jvmtiEnv* env, jrawMonitorID monitor) {
+    ENSURE_VALID_ENV(env);
     return MonitorUtil::DestroyRawMonitor(env, monitor);
   }
 
   static jvmtiError RawMonitorEnter(jvmtiEnv* env, jrawMonitorID monitor) {
+    ENSURE_VALID_ENV(env);
     return MonitorUtil::RawMonitorEnter(env, monitor);
   }
 
   static jvmtiError RawMonitorExit(jvmtiEnv* env, jrawMonitorID monitor) {
+    ENSURE_VALID_ENV(env);
     return MonitorUtil::RawMonitorExit(env, monitor);
   }
 
   static jvmtiError RawMonitorWait(jvmtiEnv* env, jrawMonitorID monitor, jlong millis) {
+    ENSURE_VALID_ENV(env);
     return MonitorUtil::RawMonitorWait(env, monitor, millis);
   }
 
   static jvmtiError RawMonitorNotify(jvmtiEnv* env, jrawMonitorID monitor) {
+    ENSURE_VALID_ENV(env);
     return MonitorUtil::RawMonitorNotify(env, monitor);
   }
 
   static jvmtiError RawMonitorNotifyAll(jvmtiEnv* env, jrawMonitorID monitor) {
+    ENSURE_VALID_ENV(env);
     return MonitorUtil::RawMonitorNotifyAll(env, monitor);
   }
 
   static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table) {
+    ENSURE_VALID_ENV(env);
     return JNIUtil::SetJNIFunctionTable(env, function_table);
   }
 
   static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) {
+    ENSURE_VALID_ENV(env);
     return JNIUtil::GetJNIFunctionTable(env, function_table);
   }
 
@@ -955,14 +1068,16 @@
     return gEventHandler.SetEvent(art_env, art_thread, GetArtJvmtiEvent(art_env, event_type), mode);
   }
 
-  static jvmtiError GenerateEvents(jvmtiEnv* env ATTRIBUTE_UNUSED,
+  static jvmtiError GenerateEvents(jvmtiEnv* env,
                                    jvmtiEvent event_type ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     return OK;
   }
 
-  static jvmtiError GetExtensionFunctions(jvmtiEnv* env ATTRIBUTE_UNUSED,
+  static jvmtiError GetExtensionFunctions(jvmtiEnv* env,
                                           jint* extension_count_ptr,
                                           jvmtiExtensionFunctionInfo** extensions) {
+    ENSURE_VALID_ENV(env);
     // We do not have any extension functions.
     *extension_count_ptr = 0;
     *extensions = nullptr;
@@ -970,9 +1085,10 @@
     return ERR(NONE);
   }
 
-  static jvmtiError GetExtensionEvents(jvmtiEnv* env ATTRIBUTE_UNUSED,
+  static jvmtiError GetExtensionEvents(jvmtiEnv* env,
                                        jint* extension_count_ptr,
                                        jvmtiExtensionEventInfo** extensions) {
+    ENSURE_VALID_ENV(env);
     // We do not have any extension events.
     *extension_count_ptr = 0;
     *extensions = nullptr;
@@ -980,9 +1096,10 @@
     return ERR(NONE);
   }
 
-  static jvmtiError SetExtensionEventCallback(jvmtiEnv* env ATTRIBUTE_UNUSED,
+  static jvmtiError SetExtensionEventCallback(jvmtiEnv* env,
                                               jint extension_event_index ATTRIBUTE_UNUSED,
                                               jvmtiExtensionEvent callback ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     // We do not have any extension events, so any call is illegal.
     return ERR(ILLEGAL_ARGUMENT);
   }
@@ -999,8 +1116,8 @@
     ENSURE_NON_NULL(capabilities_ptr);
     ArtJvmTiEnv* art_env = static_cast<ArtJvmTiEnv*>(env);
     jvmtiError ret = OK;
-    jvmtiCapabilities changed;
-    jvmtiCapabilities potential_capabilities;
+    jvmtiCapabilities changed = {};
+    jvmtiCapabilities potential_capabilities = {};
     ret = env->GetPotentialCapabilities(&potential_capabilities);
     if (ret != OK) {
       return ret;
@@ -1072,7 +1189,7 @@
     ENSURE_VALID_ENV(env);
     ENSURE_NON_NULL(capabilities_ptr);
     ArtJvmTiEnv* art_env = reinterpret_cast<ArtJvmTiEnv*>(env);
-    jvmtiCapabilities changed;
+    jvmtiCapabilities changed = {};
 #define DEL_CAPABILITY(e) \
     do { \
       if (capabilities_ptr->e == 1) { \
@@ -1141,17 +1258,20 @@
 
   static jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiEnv* env,
                                                  jvmtiTimerInfo* info_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_current_thread_cpu_time);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError GetCurrentThreadCpuTime(jvmtiEnv* env, jlong* nanos_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_current_thread_cpu_time);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError GetThreadCpuTimerInfo(jvmtiEnv* env,
                                           jvmtiTimerInfo* info_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_thread_cpu_time);
     return ERR(NOT_IMPLEMENTED);
   }
@@ -1159,43 +1279,53 @@
   static jvmtiError GetThreadCpuTime(jvmtiEnv* env,
                                      jthread thread ATTRIBUTE_UNUSED,
                                      jlong* nanos_ptr ATTRIBUTE_UNUSED) {
+    ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_thread_cpu_time);
     return ERR(NOT_IMPLEMENTED);
   }
 
   static jvmtiError GetTimerInfo(jvmtiEnv* env, jvmtiTimerInfo* info_ptr) {
+    ENSURE_VALID_ENV(env);
     return TimerUtil::GetTimerInfo(env, info_ptr);
   }
 
   static jvmtiError GetTime(jvmtiEnv* env, jlong* nanos_ptr) {
+    ENSURE_VALID_ENV(env);
     return TimerUtil::GetTime(env, nanos_ptr);
   }
 
   static jvmtiError GetAvailableProcessors(jvmtiEnv* env, jint* processor_count_ptr) {
+    ENSURE_VALID_ENV(env);
     return TimerUtil::GetAvailableProcessors(env, processor_count_ptr);
   }
 
   static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment) {
+    ENSURE_VALID_ENV(env);
     return SearchUtil::AddToBootstrapClassLoaderSearch(env, segment);
   }
 
   static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment) {
+    ENSURE_VALID_ENV(env);
     return SearchUtil::AddToSystemClassLoaderSearch(env, segment);
   }
 
   static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) {
+    ENSURE_VALID_ENV(env);
     return PropertiesUtil::GetSystemProperties(env, count_ptr, property_ptr);
   }
 
   static jvmtiError GetSystemProperty(jvmtiEnv* env, const char* property, char** value_ptr) {
+    ENSURE_VALID_ENV(env);
     return PropertiesUtil::GetSystemProperty(env, property, value_ptr);
   }
 
   static jvmtiError SetSystemProperty(jvmtiEnv* env, const char* property, const char* value) {
+    ENSURE_VALID_ENV(env);
     return PropertiesUtil::SetSystemProperty(env, property, value);
   }
 
   static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr) {
+    ENSURE_VALID_ENV(env);
     return PhaseUtil::GetPhase(env, phase_ptr);
   }
 
@@ -1303,9 +1433,10 @@
     }
   }
 
-  static jvmtiError SetVerboseFlag(jvmtiEnv* env ATTRIBUTE_UNUSED,
+  static jvmtiError SetVerboseFlag(jvmtiEnv* env,
                                    jvmtiVerboseFlag flag,
                                    jboolean value) {
+    ENSURE_VALID_ENV(env);
     if (flag == jvmtiVerboseFlag::JVMTI_VERBOSE_OTHER) {
       // OTHER is special, as it's 0, so can't do a bit check.
       bool val = (value == JNI_TRUE) ? true : false;
@@ -1359,8 +1490,8 @@
     return ERR(NONE);
   }
 
-  static jvmtiError GetJLocationFormat(jvmtiEnv* env ATTRIBUTE_UNUSED,
-                                       jvmtiJlocationFormat* format_ptr) {
+  static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) {
+    ENSURE_VALID_ENV(env);
     // Report BCI as jlocation format. We report dex bytecode indices.
     if (format_ptr == nullptr) {
       return ERR(NULL_POINTER);
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 4f5eb0c..d88805e 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -126,6 +126,7 @@
                                                          unsigned char** new_class_data) const {
   static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
                 kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event");
+  DCHECK(*new_class_data == nullptr);
   jint current_len = class_data_len;
   unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
   ArtJvmTiEnv* last_env = nullptr;
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index 2d1b25e..38fd1d4 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -259,7 +259,7 @@
       }
 
       // Actually set the ClassExt's original bytes once we have actually succeeded.
-      ext->SetOriginalDexFileBytes(arr.Get());
+      ext->SetOriginalDexFile(arr.Get());
       // Set the return values
       *final_class_def = &dex_file->GetClassDef(0);
       *final_dex_file = dex_file.release();
diff --git a/runtime/openjdkjvmti/ti_class_definition.h b/runtime/openjdkjvmti/ti_class_definition.h
index 3c251d4..9ca1c07 100644
--- a/runtime/openjdkjvmti/ti_class_definition.h
+++ b/runtime/openjdkjvmti/ti_class_definition.h
@@ -49,7 +49,16 @@
   JvmtiUniquePtr<unsigned char> dex_data;
   art::ArraySlice<const unsigned char> original_dex_file;
 
-  ArtClassDefinition() = default;
+  ArtClassDefinition()
+      : klass(nullptr),
+        loader(nullptr),
+        name(),
+        protection_domain(nullptr),
+        dex_len(0),
+        dex_data(nullptr),
+        original_dex_file(),
+        modified(false) {}
+
   ArtClassDefinition(ArtClassDefinition&& o) = default;
 
   void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) {
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 9c1d6ef..7faddfb 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -469,7 +469,7 @@
   result_ = result;
 }
 
-art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFileBytes() {
+art::mirror::Object* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFile() {
   // If we have been specifically given a new set of bytes use that
   if (original_dex_file_.size() != 0) {
     return art::mirror::ByteArray::AllocateAndFill(
@@ -481,24 +481,21 @@
   // See if we already have one set.
   art::ObjPtr<art::mirror::ClassExt> ext(GetMirrorClass()->GetExtData());
   if (!ext.IsNull()) {
-    art::ObjPtr<art::mirror::ByteArray> old_original_bytes(ext->GetOriginalDexFileBytes());
-    if (!old_original_bytes.IsNull()) {
+    art::ObjPtr<art::mirror::Object> old_original_dex_file(ext->GetOriginalDexFile());
+    if (!old_original_dex_file.IsNull()) {
       // We do. Use it.
-      return old_original_bytes.Ptr();
+      return old_original_dex_file.Ptr();
     }
   }
 
-  // Copy the current dex_file
-  const art::DexFile& current_dex_file = GetMirrorClass()->GetDexFile();
+  // return the current dex_cache which has the dex file in it.
+  art::ObjPtr<art::mirror::DexCache> current_dex_cache(GetMirrorClass()->GetDexCache());
   // TODO Handle this or make it so it cannot happen.
-  if (current_dex_file.NumClassDefs() != 1) {
+  if (current_dex_cache->GetDexFile()->NumClassDefs() != 1) {
     LOG(WARNING) << "Current dex file has more than one class in it. Calling RetransformClasses "
                  << "on this class might fail if no transformations are applied to it!";
   }
-  return art::mirror::ByteArray::AllocateAndFill(
-      driver_->self_,
-      reinterpret_cast<const signed char*>(current_dex_file.Begin()),
-      current_dex_file.Size());
+  return current_dex_cache.Ptr();
 }
 
 struct CallbackCtx {
@@ -847,9 +844,9 @@
     return art::down_cast<art::mirror::Class*>(GetSlot(klass_index, kSlotMirrorClass));
   }
 
-  art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index) const
+  art::mirror::Object* GetOriginalDexFile(jint klass_index) const
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    return art::down_cast<art::mirror::ByteArray*>(GetSlot(klass_index, kSlotOrigDexFile));
+    return art::down_cast<art::mirror::Object*>(GetSlot(klass_index, kSlotOrigDexFile));
   }
 
   void SetSourceClassLoader(jint klass_index, art::mirror::ClassLoader* loader)
@@ -872,7 +869,7 @@
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
     SetSlot(klass_index, kSlotMirrorClass, klass);
   }
-  void SetOriginalDexFileBytes(jint klass_index, art::mirror::ByteArray* bytes)
+  void SetOriginalDexFile(jint klass_index, art::mirror::Object* bytes)
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
     SetSlot(klass_index, kSlotOrigDexFile, bytes);
   }
@@ -985,9 +982,9 @@
   art::mirror::Class* GetMirrorClass() const REQUIRES_SHARED(art::Locks::mutator_lock_) {
     return holder_.GetMirrorClass(idx_);
   }
-  art::mirror::ByteArray* GetOriginalDexFileBytes() const
+  art::mirror::Object* GetOriginalDexFile() const
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    return holder_.GetOriginalDexFileBytes(idx_);
+    return holder_.GetOriginalDexFile(idx_);
   }
   int32_t GetIndex() const {
     return idx_;
@@ -1010,9 +1007,9 @@
   void SetMirrorClass(art::mirror::Class* klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
     holder_.SetMirrorClass(idx_, klass);
   }
-  void SetOriginalDexFileBytes(art::mirror::ByteArray* bytes)
+  void SetOriginalDexFile(art::mirror::Object* bytes)
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    holder_.SetOriginalDexFileBytes(idx_, bytes);
+    holder_.SetOriginalDexFile(idx_, bytes);
   }
 
  private:
@@ -1138,8 +1135,8 @@
   }
 
   // We won't always need to set this field.
-  cur_data->SetOriginalDexFileBytes(AllocateOrGetOriginalDexFileBytes());
-  if (cur_data->GetOriginalDexFileBytes() == nullptr) {
+  cur_data->SetOriginalDexFile(AllocateOrGetOriginalDexFile());
+  if (cur_data->GetOriginalDexFile() == nullptr) {
     driver_->self_->AssertPendingOOMException();
     driver_->self_->ClearException();
     RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate array for original dex file");
@@ -1285,7 +1282,7 @@
     art::mirror::Class* klass = data.GetMirrorClass();
     // TODO Rewrite so we don't do a stack walk for each and every class.
     redef.FindAndAllocateObsoleteMethods(klass);
-    redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFileBytes());
+    redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFile());
   }
   // TODO We should check for if any of the redefined methods are intrinsic methods here and, if any
   // are, force a full-world deoptimization before finishing redefinition. If we don't do this then
@@ -1365,7 +1362,7 @@
 void Redefiner::ClassRedefinition::UpdateClass(
     art::ObjPtr<art::mirror::Class> mclass,
     art::ObjPtr<art::mirror::DexCache> new_dex_cache,
-    art::ObjPtr<art::mirror::ByteArray> original_dex_file) {
+    art::ObjPtr<art::mirror::Object> original_dex_file) {
   DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
   const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0);
   UpdateMethods(mclass, new_dex_cache, class_def);
@@ -1379,7 +1376,7 @@
   mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_.c_str())));
   art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData());
   CHECK(!ext.IsNull());
-  ext->SetOriginalDexFileBytes(original_dex_file);
+  ext->SetOriginalDexFile(original_dex_file);
 }
 
 // This function does all (java) allocations we need to do for the Class being redefined.
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 4313a94..6c09d46 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -137,7 +137,7 @@
         REQUIRES_SHARED(art::Locks::mutator_lock_);
 
     // This may return nullptr with a OOME pending if allocation fails.
-    art::mirror::ByteArray* AllocateOrGetOriginalDexFileBytes()
+    art::mirror::Object* AllocateOrGetOriginalDexFile()
         REQUIRES_SHARED(art::Locks::mutator_lock_);
 
     void RecordFailure(jvmtiError e, const std::string& err) {
@@ -196,7 +196,7 @@
 
     void UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
                      art::ObjPtr<art::mirror::DexCache> new_dex_cache,
-                     art::ObjPtr<art::mirror::ByteArray> original_dex_file)
+                     art::ObjPtr<art::mirror::Object> original_dex_file)
         REQUIRES(art::Locks::mutator_lock_);
 
     void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_);
diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc
index bd52cbb..06aecba 100644
--- a/runtime/openjdkjvmti/transform.cc
+++ b/runtime/openjdkjvmti/transform.cc
@@ -150,16 +150,27 @@
                                                       art::Handle<art::mirror::Class> klass,
                                                       /*out*/jint* dex_data_len,
                                                       /*out*/unsigned char** dex_data) {
-  art::StackHandleScope<2> hs(art::Thread::Current());
+  art::StackHandleScope<3> hs(art::Thread::Current());
   art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData()));
   if (!ext.IsNull()) {
-    art::Handle<art::mirror::ByteArray> orig_dex(hs.NewHandle(ext->GetOriginalDexFileBytes()));
+    art::Handle<art::mirror::Object> orig_dex(hs.NewHandle(ext->GetOriginalDexFile()));
     if (!orig_dex.IsNull()) {
-      *dex_data_len = static_cast<jint>(orig_dex->GetLength());
-      return CopyDataIntoJvmtiBuffer(env,
-                                     reinterpret_cast<const unsigned char*>(orig_dex->GetData()),
-                                     *dex_data_len,
-                                     /*out*/dex_data);
+      if (orig_dex->IsArrayInstance()) {
+        DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
+        art::Handle<art::mirror::ByteArray> orig_dex_bytes(
+            hs.NewHandle(art::down_cast<art::mirror::ByteArray*>(orig_dex->AsArray())));
+        *dex_data_len = static_cast<jint>(orig_dex_bytes->GetLength());
+        return CopyDataIntoJvmtiBuffer(
+            env,
+            reinterpret_cast<const unsigned char*>(orig_dex_bytes->GetData()),
+            *dex_data_len,
+            /*out*/dex_data);
+      } else {
+        DCHECK(orig_dex->IsDexCache());
+        const art::DexFile* dex_file = orig_dex->AsDexCache()->GetDexFile();
+        *dex_data_len = static_cast<jint>(dex_file->Size());
+        return CopyDataIntoJvmtiBuffer(env, dex_file->Begin(), dex_file->Size(), /*out*/dex_data);
+      }
     }
   }
   // TODO De-quicken the dex file before passing it to the agents.
diff --git a/runtime/primitive.cc b/runtime/primitive.cc
index 2380284..1ec345a 100644
--- a/runtime/primitive.cc
+++ b/runtime/primitive.cc
@@ -44,7 +44,7 @@
   "Ljava/lang/Void;",
 };
 
-#define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
+#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
 
 const char* Primitive::PrettyDescriptor(Primitive::Type type) {
   static_assert(COUNT_OF(kTypeNames) == static_cast<size_t>(Primitive::kPrimLast) + 1,
diff --git a/runtime/stack.h b/runtime/stack.h
index 5c9614a..bdaa4c3 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -430,8 +430,15 @@
  private:
   ShadowFrame(uint32_t num_vregs, ShadowFrame* link, ArtMethod* method,
               uint32_t dex_pc, bool has_reference_array)
-      : link_(link), method_(method), result_register_(nullptr), dex_pc_ptr_(nullptr),
-        code_item_(nullptr), number_of_vregs_(num_vregs), dex_pc_(dex_pc) {
+      : link_(link),
+        method_(method),
+        result_register_(nullptr),
+        dex_pc_ptr_(nullptr),
+        code_item_(nullptr),
+        number_of_vregs_(num_vregs),
+        dex_pc_(dex_pc),
+        cached_hotness_countdown_(0),
+        hotness_countdown_(0) {
     // TODO(iam): Remove this parameter, it's an an artifact of portable removal
     DCHECK(has_reference_array);
     if (has_reference_array) {
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index ffa17c9..a224986 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -688,7 +688,13 @@
 
 class StackMapEncoding {
  public:
-  StackMapEncoding() {}
+  StackMapEncoding()
+      : dex_pc_bit_offset_(0),
+        dex_register_map_bit_offset_(0),
+        inline_info_bit_offset_(0),
+        register_mask_index_bit_offset_(0),
+        stack_mask_index_bit_offset_(0),
+        total_bit_size_(0) {}
 
   // Set stack map bit layout based on given sizes.
   // Returns the size of stack map in bits.
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 7aa98cd..0333fe8 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -162,7 +162,7 @@
       FieldValueKind kind;
       bool is_volatile;
 
-      FieldValue() = default;
+      FieldValue() : value(0), kind(FieldValueKind::kBoolean), is_volatile(false) {}
       FieldValue(FieldValue&& log) = default;
 
      private:
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index c1efecd..1727f88 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -26,77 +26,38 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <utility>
+
 #include "sigchain.h"
 
 #if defined(__APPLE__)
 #define _NSIG NSIG
 #define sighandler_t sig_t
+
+// Darwin has an #error when ucontext.h is included without _XOPEN_SOURCE defined.
+#define _XOPEN_SOURCE
 #endif
 
-namespace art {
+#include <ucontext.h>
 
-typedef int (*SigActionFnPtr)(int, const struct sigaction*, struct sigaction*);
+// libsigchain provides an interception layer for signal handlers, to allow ART and others to give
+// their signal handlers the first stab at handling signals before passing them on to user code.
+//
+// It implements wrapper functions for signal, sigaction, and sigprocmask, and a handler that
+// forwards signals appropriately.
+//
+// In our handler, we start off with all signals blocked, fetch the original signal mask from the
+// passed in ucontext, and then adjust our signal mask appropriately for the user handler.
+//
+// It's somewhat tricky for us to properly handle some flag cases:
+//   SA_NOCLDSTOP and SA_NOCLDWAIT: shouldn't matter, we don't have special handlers for SIGCHLD.
+//   SA_NODEFER: unimplemented, we can manually change the signal mask appropriately.
+//  ~SA_ONSTACK: always silently enable this
+//   SA_RESETHAND: unimplemented, but we can probably do this?
+//  ~SA_RESTART: unimplemented, maybe we can reserve an RT signal, register an empty handler that
+//               doesn't have SA_RESTART, and raise the signal to avoid restarting syscalls that are
+//               expected to be interrupted?
 
-class SignalAction {
- public:
-  SignalAction() : claimed_(false), uses_old_style_(false), special_handler_(nullptr) {
-  }
-
-  // Claim the signal and keep the action specified.
-  void Claim(const struct sigaction& action) {
-    action_ = action;
-    claimed_ = true;
-  }
-
-  // Unclaim the signal and restore the old action.
-  void Unclaim(int signal) {
-    claimed_ = false;
-    sigaction(signal, &action_, nullptr);        // Restore old action.
-  }
-
-  // Get the action associated with this signal.
-  const struct sigaction& GetAction() const {
-    return action_;
-  }
-
-  // Is the signal claimed?
-  bool IsClaimed() const {
-    return claimed_;
-  }
-
-  // Change the recorded action to that specified.
-  // If oldstyle is true then this action is from an older style signal()
-  // call as opposed to sigaction().  In this case the sa_handler is
-  // used when invoking the user's handler.
-  void SetAction(const struct sigaction& action, bool oldstyle) {
-    action_ = action;
-    uses_old_style_ = oldstyle;
-  }
-
-  bool OldStyle() const {
-    return uses_old_style_;
-  }
-
-  void SetSpecialHandler(SpecialSignalHandlerFn fn) {
-    special_handler_ = fn;
-  }
-
-  SpecialSignalHandlerFn GetSpecialHandler() {
-    return special_handler_;
-  }
-
- private:
-  struct sigaction action_;                 // Action to be performed.
-  bool claimed_;                            // Whether signal is claimed or not.
-  bool uses_old_style_;                     // Action is created using signal().  Use sa_handler.
-  SpecialSignalHandlerFn special_handler_;  // A special handler executed before user handlers.
-};
-
-// User's signal handlers
-static SignalAction user_sigactions[_NSIG];
-static bool initialized;
-static void* linked_sigaction_sym;
-static void* linked_sigprocmask_sym;
 
 static void log(const char* format, ...) {
   char buf[256];
@@ -111,102 +72,186 @@
   va_end(ap);
 }
 
-static void CheckSignalValid(int signal) {
-  if (signal <= 0 || signal >= _NSIG) {
-    log("Invalid signal %d", signal);
-    abort();
-  }
-}
+#define fatal(...) log(__VA_ARGS__); abort()
 
-// Sigchainlib's own handler so we can ensure a managed handler is called first even if nobody
-// claimed a chain. Simply forward to InvokeUserSignalHandler.
-static void sigchainlib_managed_handler_sigaction(int sig, siginfo_t* info, void* context) {
-  InvokeUserSignalHandler(sig, info, context);
-}
-
-// Claim a signal chain for a particular signal.
-extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) {
-  CheckSignalValid(signal);
-
-  user_sigactions[signal].Claim(*oldaction);
-}
-
-extern "C" void UnclaimSignalChain(int signal) {
-  CheckSignalValid(signal);
-
-  user_sigactions[signal].Unclaim(signal);
-}
-
-// Invoke the user's signal handler.
-extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
-  // Check the arguments.
-  CheckSignalValid(sig);
-
-  // The signal must have been claimed in order to get here.  Check it.
-  if (!user_sigactions[sig].IsClaimed()) {
-    abort();
-  }
-
-  // Do we have a managed handler? If so, run it first.
-  SpecialSignalHandlerFn managed = user_sigactions[sig].GetSpecialHandler();
-  if (managed != nullptr) {
-    sigset_t mask, old_mask;
-    sigfillset(&mask);
-    sigprocmask(SIG_BLOCK, &mask, &old_mask);
-    // Call the handler. If it succeeds, we're done.
-    if (managed(sig, info, context)) {
-      sigprocmask(SIG_SETMASK, &old_mask, nullptr);
-      return;
+static int sigorset(sigset_t* dest, sigset_t* left, sigset_t* right) {
+  sigemptyset(dest);
+  for (size_t i = 0; i < sizeof(sigset_t) * CHAR_BIT; ++i) {
+    if (sigismember(left, i) == 1 || sigismember(right, i) == 1) {
+      sigaddset(dest, i);
     }
-    sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+  }
+  return 0;
+}
+
+namespace art {
+
+static decltype(&sigaction) linked_sigaction;
+static decltype(&sigprocmask) linked_sigprocmask;
+__thread bool handling_signal;
+
+class SignalChain {
+ public:
+  SignalChain() : claimed_(false) {
   }
 
-  const struct sigaction& action = user_sigactions[sig].GetAction();
-  if (user_sigactions[sig].OldStyle()) {
-    if (action.sa_handler != nullptr) {
-      action.sa_handler(sig);
-    } else {
-      signal(sig, SIG_DFL);
-      raise(sig);
+  bool IsClaimed() {
+    return claimed_;
+  }
+
+  void Claim(int signo) {
+    if (!claimed_) {
+      Register(signo);
+      claimed_ = true;
     }
+  }
+
+  // Register the signal chain with the kernel if needed.
+  void Register(int signo) {
+    struct sigaction handler_action = {};
+    handler_action.sa_sigaction = SignalChain::Handler;
+    handler_action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+    sigfillset(&handler_action.sa_mask);
+    linked_sigaction(signo, &handler_action, &action_);
+  }
+
+  void SetAction(const struct sigaction* action) {
+    action_ = *action;
+  }
+
+  struct sigaction GetAction() {
+    return action_;
+  }
+
+  void AddSpecialHandler(SpecialSignalHandlerFn fn) {
+    for (SpecialSignalHandlerFn& slot : special_handlers_) {
+      if (slot == nullptr) {
+        slot = fn;
+        return;
+      }
+    }
+
+    fatal("too many special signal handlers");
+  }
+
+  void RemoveSpecialHandler(SpecialSignalHandlerFn fn) {
+    // This isn't thread safe, but it's unlikely to be a real problem.
+    size_t len = sizeof(special_handlers_)/sizeof(*special_handlers_);
+    for (size_t i = 0; i < len; ++i) {
+      if (special_handlers_[i] == fn) {
+        for (size_t j = i; j < len - 1; ++j) {
+          special_handlers_[j] = special_handlers_[j + 1];
+        }
+        special_handlers_[len - 1] = nullptr;
+        return;
+      }
+    }
+
+    fatal("failed to find special handler to remove");
+  }
+
+
+  static void Handler(int signo, siginfo_t* siginfo, void*);
+
+ private:
+  bool claimed_;
+  struct sigaction action_;
+  SpecialSignalHandlerFn special_handlers_[2];
+};
+
+static SignalChain chains[_NSIG];
+
+class ScopedFlagRestorer {
+ public:
+  explicit ScopedFlagRestorer(bool* flag) : flag_(flag), original_value_(*flag) {
+  }
+
+  ~ScopedFlagRestorer() {
+    *flag_ = original_value_;
+  }
+
+ private:
+  bool* flag_;
+  bool original_value_;
+};
+
+class ScopedSignalUnblocker {
+ public:
+  explicit ScopedSignalUnblocker(const std::initializer_list<int>& signals) {
+    sigset_t new_mask;
+    sigemptyset(&new_mask);
+    for (int signal : signals) {
+      sigaddset(&new_mask, signal);
+    }
+    if (sigprocmask(SIG_UNBLOCK, &new_mask, &previous_mask_) != 0) {
+      fatal("failed to unblock signals: %s", strerror(errno));
+    }
+  }
+
+  ~ScopedSignalUnblocker() {
+    if (sigprocmask(SIG_SETMASK, &previous_mask_, nullptr) != 0) {
+      fatal("failed to unblock signals: %s", strerror(errno));
+    }
+  }
+
+ private:
+  sigset_t previous_mask_;
+};
+
+void SignalChain::Handler(int signo, siginfo_t* siginfo, void* ucontext_raw) {
+  ScopedFlagRestorer flag(&handling_signal);
+
+  // Try the special handlers first.
+  // If one of them crashes, we'll reenter this handler and pass that crash onto the user handler.
+  if (!handling_signal) {
+    ScopedSignalUnblocker unblocked { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }; // NOLINT
+    handling_signal = true;
+
+    for (const auto& handler : chains[signo].special_handlers_) {
+      if (handler != nullptr && handler(signo, siginfo, ucontext_raw)) {
+        return;
+      }
+    }
+  }
+
+  // Forward to the user's signal handler.
+  int handler_flags = chains[signo].action_.sa_flags;
+  ucontext_t* ucontext = static_cast<ucontext_t*>(ucontext_raw);
+  sigset_t mask;
+  sigorset(&mask, &ucontext->uc_sigmask, &chains[signo].action_.sa_mask);
+  if ((handler_flags & SA_NODEFER)) {
+    sigdelset(&mask, signo);
+  }
+  sigprocmask(SIG_SETMASK, &mask, nullptr);
+
+  if ((handler_flags & SA_SIGINFO)) {
+    chains[signo].action_.sa_sigaction(signo, siginfo, ucontext_raw);
   } else {
-    if (action.sa_sigaction != nullptr) {
-      sigset_t old_mask;
-      sigprocmask(SIG_BLOCK, &action.sa_mask, &old_mask);
-      action.sa_sigaction(sig, info, context);
-      sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+    auto handler = chains[signo].action_.sa_handler;
+    if (handler == SIG_IGN) {
+      return;
+    } else if (handler == SIG_DFL) {
+      fatal("exiting due to SIG_DFL handler for signal %d", signo);
     } else {
-      signal(sig, SIG_DFL);
-      raise(sig);
+      handler(signo);
     }
   }
 }
 
-extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
-  CheckSignalValid(signal);
-  // Read the current action without looking at the chain, it should be the expected action.
-  SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
-  struct sigaction current_action;
-  linked_sigaction(signal, nullptr, &current_action);
-  // If the sigactions don't match then we put the current action on the chain and make ourself as
-  // the main action.
-  if (current_action.sa_sigaction != expected_action->sa_sigaction) {
-    log("Warning: Unexpected sigaction action found %p\n", current_action.sa_sigaction);
-    user_sigactions[signal].Claim(current_action);
-    linked_sigaction(signal, expected_action, nullptr);
-  }
-}
-
 extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
   // If this signal has been claimed as a signal chain, record the user's
   // action but don't pass it on to the kernel.
   // Note that we check that the signal number is in range here.  An out of range signal
   // number should behave exactly as the libc sigaction.
-  if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed() &&
-      (new_action == nullptr || new_action->sa_handler != SIG_DFL)) {
-    struct sigaction saved_action = user_sigactions[signal].GetAction();
+  if (signal < 0 || signal >= _NSIG) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  if (chains[signal].IsClaimed()) {
+    struct sigaction saved_action = chains[signal].GetAction();
     if (new_action != nullptr) {
-      user_sigactions[signal].SetAction(*new_action, false);
+      chains[signal].SetAction(new_action);
     }
     if (old_action != nullptr) {
       *old_action = saved_action;
@@ -216,73 +261,52 @@
 
   // Will only get here if the signal chain has not been claimed.  We want
   // to pass the sigaction on to the kernel via the real sigaction in libc.
-
-  if (linked_sigaction_sym == nullptr) {
-    // Perform lazy initialization.
-    // This will only occur outside of a signal context since we have
-    // not been initialized and therefore cannot be within the ART
-    // runtime.
-    InitializeSignalChain();
-  }
-
-  if (linked_sigaction_sym == nullptr) {
-    log("Unable to find next sigaction in signal chain");
-    abort();
-  }
-  SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
+  InitializeSignalChain();
   return linked_sigaction(signal, new_action, old_action);
 }
 
-static sighandler_t signal_impl(int signal, sighandler_t handler) {
-  struct sigaction sa;
+extern "C" sighandler_t signal(int signo, sighandler_t handler) {
+  if (signo < 0 || signo > _NSIG) {
+    errno = EINVAL;
+    return SIG_ERR;
+  }
+
+  struct sigaction sa = {};
   sigemptyset(&sa.sa_mask);
   sa.sa_handler = handler;
-  sa.sa_flags = SA_RESTART;
+  sa.sa_flags = SA_RESTART | SA_ONSTACK;
   sighandler_t oldhandler;
 
   // If this signal has been claimed as a signal chain, record the user's
   // action but don't pass it on to the kernel.
-  // Note that we check that the signal number is in range here.  An out of range signal
-  // number should behave exactly as the libc sigaction.
-  if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed() && handler != SIG_DFL) {
-    oldhandler = reinterpret_cast<sighandler_t>(user_sigactions[signal].GetAction().sa_handler);
-    user_sigactions[signal].SetAction(sa, true);
+  if (chains[signo].IsClaimed()) {
+    oldhandler = reinterpret_cast<sighandler_t>(chains[signo].GetAction().sa_handler);
+    chains[signo].SetAction(&sa);
     return oldhandler;
   }
 
   // Will only get here if the signal chain has not been claimed.  We want
   // to pass the sigaction on to the kernel via the real sigaction in libc.
-
-  if (linked_sigaction_sym == nullptr) {
-    // Perform lazy initialization.
-    InitializeSignalChain();
-  }
-
-  if (linked_sigaction_sym == nullptr) {
-    log("Unable to find next sigaction in signal chain");
-    abort();
-  }
-
-  typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
-  SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
-  if (linked_sigaction(signal, &sa, &sa) == -1) {
+  InitializeSignalChain();
+  if (linked_sigaction(signo, &sa, &sa) == -1) {
     return SIG_ERR;
   }
 
   return reinterpret_cast<sighandler_t>(sa.sa_handler);
 }
 
-extern "C" sighandler_t signal(int signal, sighandler_t handler) {
-  return signal_impl(signal, handler);
-}
-
 #if !defined(__LP64__)
-extern "C" sighandler_t bsd_signal(int signal, sighandler_t handler) {
-  return signal_impl(signal, handler);
+extern "C" sighandler_t bsd_signal(int signo, sighandler_t handler) {
+  return signal(signo, handler);
 }
 #endif
 
 extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
+  // When inside a signal handler, forward directly to the actual sigprocmask.
+  if (handling_signal) {
+    return linked_sigprocmask(how, bionic_new_set, bionic_old_set);
+  }
+
   const sigset_t* new_set_ptr = bionic_new_set;
   sigset_t tmpset;
   if (bionic_new_set != nullptr) {
@@ -292,7 +316,7 @@
       // Don't allow claimed signals in the mask.  If a signal chain has been claimed
       // we can't allow the user to block that signal.
       for (int i = 0 ; i < _NSIG; ++i) {
-        if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
+        if (chains[i].IsClaimed() && sigismember(&tmpset, i)) {
           sigdelset(&tmpset, i);
         }
       }
@@ -300,18 +324,7 @@
     new_set_ptr = &tmpset;
   }
 
-  if (linked_sigprocmask_sym == nullptr) {
-    // Perform lazy initialization.
-    InitializeSignalChain();
-  }
-
-  if (linked_sigprocmask_sym == nullptr) {
-    log("Unable to find next sigprocmask in signal chain");
-    abort();
-  }
-
-  typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
-  SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
+  InitializeSignalChain();
   return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
 }
 
@@ -322,49 +335,67 @@
   // taken and if it so happens that a signal occurs while one of these
   // locks is already taken, dlsym will block trying to reenter a
   // mutex and we will never get out of it.
+  static bool initialized = false;
   if (initialized) {
     // Don't initialize twice.
     return;
   }
-  linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
+
+  void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
   if (linked_sigaction_sym == nullptr) {
     linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
     if (linked_sigaction_sym == nullptr ||
         linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
-      linked_sigaction_sym = nullptr;
+      fatal("Unable to find next sigaction in signal chain");
     }
   }
 
-  linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
+  void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
   if (linked_sigprocmask_sym == nullptr) {
     linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
     if (linked_sigprocmask_sym == nullptr ||
         linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
-      linked_sigprocmask_sym = nullptr;
+      fatal("Unable to find next sigprocmask in signal chain");
     }
   }
+
+  linked_sigaction = reinterpret_cast<decltype(linked_sigaction)>(linked_sigaction_sym);
+  linked_sigprocmask = reinterpret_cast<decltype(linked_sigprocmask)>(linked_sigprocmask_sym);
   initialized = true;
 }
 
-extern "C" void SetSpecialSignalHandlerFn(int signal, SpecialSignalHandlerFn fn) {
-  CheckSignalValid(signal);
+extern "C" void AddSpecialSignalHandlerFn(int signal, SpecialSignalHandlerFn fn) {
+  if (signal <= 0 || signal >= _NSIG) {
+    fatal("Invalid signal %d", signal);
+  }
 
   // Set the managed_handler.
-  user_sigactions[signal].SetSpecialHandler(fn);
+  chains[signal].AddSpecialHandler(fn);
+  chains[signal].Claim(signal);
+}
 
-  // In case the chain isn't claimed, claim it for ourself so we can ensure the managed handler
-  // goes first.
-  if (!user_sigactions[signal].IsClaimed()) {
-    struct sigaction act, old_act;
-    act.sa_sigaction = sigchainlib_managed_handler_sigaction;
-    sigemptyset(&act.sa_mask);
-    act.sa_flags = SA_SIGINFO | SA_ONSTACK;
-#if !defined(__APPLE__) && !defined(__mips__)
-    act.sa_restorer = nullptr;
-#endif
-    if (sigaction(signal, &act, &old_act) != -1) {
-      user_sigactions[signal].Claim(old_act);
-    }
+extern "C" void RemoveSpecialSignalHandlerFn(int signal, SpecialSignalHandlerFn fn) {
+  if (signal <= 0 || signal >= _NSIG) {
+    fatal("Invalid signal %d", signal);
+  }
+
+  chains[signal].RemoveSpecialHandler(fn);
+}
+
+extern "C" void EnsureFrontOfChain(int signal) {
+  if (signal <= 0 || signal >= _NSIG) {
+    fatal("Invalid signal %d", signal);
+  }
+
+  // Read the current action without looking at the chain, it should be the expected action.
+  struct sigaction current_action;
+  InitializeSignalChain();
+  linked_sigaction(signal, nullptr, &current_action);
+  // If the sigactions don't match then we put the current action on the chain and make ourself as
+  // the main action.
+  if (current_action.sa_sigaction != SignalChain::Handler) {
+    log("Warning: Unexpected sigaction action found %p\n", current_action.sa_sigaction);
+    chains[signal].Register(signal);
   }
 }
 
diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h
index 01ccedf..960d221 100644
--- a/sigchainlib/sigchain.h
+++ b/sigchainlib/sigchain.h
@@ -23,16 +23,11 @@
 
 extern "C" void InitializeSignalChain();
 
-extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction);
-
-extern "C" void UnclaimSignalChain(int signal);
-
 typedef bool (*SpecialSignalHandlerFn)(int, siginfo_t*, void*);
-extern "C" void SetSpecialSignalHandlerFn(int signal, SpecialSignalHandlerFn fn);
+extern "C" void AddSpecialSignalHandlerFn(int signal, SpecialSignalHandlerFn fn);
+extern "C" void RemoveSpecialSignalHandlerFn(int signal, SpecialSignalHandlerFn fn);
 
-extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context);
-
-extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action);
+extern "C" void EnsureFrontOfChain(int signal);
 
 }  // namespace art
 
diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc
index aa3c360..d6a5e12 100644
--- a/sigchainlib/sigchain_dummy.cc
+++ b/sigchainlib/sigchain_dummy.cc
@@ -48,37 +48,23 @@
 
 namespace art {
 
-
-extern "C" void ClaimSignalChain(int signal ATTRIBUTE_UNUSED,
-                                 struct sigaction* oldaction ATTRIBUTE_UNUSED) {
-  log("ClaimSignalChain is not exported by the main executable.");
-  abort();
-}
-
-extern "C" void UnclaimSignalChain(int signal ATTRIBUTE_UNUSED) {
-  log("UnclaimSignalChain is not exported by the main executable.");
-  abort();
-}
-
-extern "C" void InvokeUserSignalHandler(int sig ATTRIBUTE_UNUSED,
-                                        siginfo_t* info ATTRIBUTE_UNUSED,
-                                        void* context ATTRIBUTE_UNUSED) {
-  log("InvokeUserSignalHandler is not exported by the main executable.");
-  abort();
-}
-
 extern "C" void InitializeSignalChain() {
   log("InitializeSignalChain is not exported by the main executable.");
   abort();
 }
 
-extern "C" void EnsureFrontOfChain(int signal ATTRIBUTE_UNUSED,
-                                   struct sigaction* expected_action ATTRIBUTE_UNUSED) {
+extern "C" void EnsureFrontOfChain(int signal ATTRIBUTE_UNUSED) {
   log("EnsureFrontOfChain is not exported by the main executable.");
   abort();
 }
 
-extern "C" void SetSpecialSignalHandlerFn(int signal ATTRIBUTE_UNUSED,
+extern "C" void AddSpecialSignalHandlerFn(int signal ATTRIBUTE_UNUSED,
+                                          SpecialSignalHandlerFn fn ATTRIBUTE_UNUSED) {
+  log("SetSpecialSignalHandlerFn is not exported by the main executable.");
+  abort();
+}
+
+extern "C" void RemoveSpecialSignalHandlerFn(int signal ATTRIBUTE_UNUSED,
                                           SpecialSignalHandlerFn fn ATTRIBUTE_UNUSED) {
   log("SetSpecialSignalHandlerFn is not exported by the main executable.");
   abort();
diff --git a/sigchainlib/version-script32.txt b/sigchainlib/version-script32.txt
index eec9103..f360efa 100644
--- a/sigchainlib/version-script32.txt
+++ b/sigchainlib/version-script32.txt
@@ -1,11 +1,9 @@
 {
 global:
-  ClaimSignalChain;
-  UnclaimSignalChain;
-  InvokeUserSignalHandler;
   InitializeSignalChain;
   EnsureFrontOfChain;
-  SetSpecialSignalHandlerFn;
+  AddSpecialSignalHandlerFn;
+  RemoveSpecialSignalHandlerFn;
   bsd_signal;
   sigaction;
   signal;
diff --git a/sigchainlib/version-script64.txt b/sigchainlib/version-script64.txt
index 08c312e..319d1c6 100644
--- a/sigchainlib/version-script64.txt
+++ b/sigchainlib/version-script64.txt
@@ -1,11 +1,9 @@
 {
 global:
-  ClaimSignalChain;
-  UnclaimSignalChain;
-  InvokeUserSignalHandler;
   InitializeSignalChain;
   EnsureFrontOfChain;
-  SetSpecialSignalHandlerFn;
+  AddSpecialSignalHandlerFn;
+  RemoveSpecialSignalHandlerFn;
   sigaction;
   signal;
   sigprocmask;
diff --git a/test/004-SignalTest/expected.txt b/test/004-SignalTest/expected.txt
index b3a0e1c..847b56f 100644
--- a/test/004-SignalTest/expected.txt
+++ b/test/004-SignalTest/expected.txt
@@ -3,4 +3,8 @@
 Caught NullPointerException
 Caught StackOverflowError
 signal caught
+unblocked signal received
+unblocking blocked signal
+blocked signal received
+signal handler done
 Signal test OK
diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc
index 6dd6355..a58a075 100644
--- a/test/004-SignalTest/signaltest.cc
+++ b/test/004-SignalTest/signaltest.cc
@@ -18,13 +18,14 @@
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/ucontext.h>
 #include <unistd.h>
 
 #include "base/macros.h"
 
 static int signal_count;
-static const int kMaxSignal = 2;
+static const int kMaxSignal = 1;
 
 #if defined(__i386__) || defined(__x86_64__)
 #if defined(__APPLE__)
@@ -47,6 +48,17 @@
 #endif
 #endif
 
+#define BLOCKED_SIGNAL SIGUSR1
+#define UNBLOCKED_SIGNAL SIGUSR2
+
+static void blocked_signal(int sig ATTRIBUTE_UNUSED) {
+  printf("blocked signal received\n");
+}
+
+static void unblocked_signal(int sig ATTRIBUTE_UNUSED) {
+  printf("unblocked signal received\n");
+}
+
 static void signalhandler(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
                           void* context) {
   printf("signal caught\n");
@@ -54,6 +66,16 @@
   if (signal_count > kMaxSignal) {
      abort();
   }
+
+  raise(UNBLOCKED_SIGNAL);
+  raise(BLOCKED_SIGNAL);
+  printf("unblocking blocked signal\n");
+
+  sigset_t mask;
+  sigemptyset(&mask);
+  sigaddset(&mask, BLOCKED_SIGNAL);
+  sigprocmask(SIG_UNBLOCK, &mask, nullptr);
+
 #if defined(__arm__)
   struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
   struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
@@ -71,6 +93,8 @@
 #else
   UNUSED(context);
 #endif
+
+  printf("signal handler done\n");
 }
 
 static struct sigaction oldaction;
@@ -78,13 +102,21 @@
 extern "C" JNIEXPORT void JNICALL Java_Main_initSignalTest(JNIEnv*, jclass) {
   struct sigaction action;
   action.sa_sigaction = signalhandler;
-  sigemptyset(&action.sa_mask);
+  sigfillset(&action.sa_mask);
+  sigdelset(&action.sa_mask, UNBLOCKED_SIGNAL);
   action.sa_flags = SA_SIGINFO | SA_ONSTACK;
 #if !defined(__APPLE__) && !defined(__mips__)
   action.sa_restorer = nullptr;
 #endif
 
   sigaction(SIGSEGV, &action, &oldaction);
+  struct sigaction check;
+  sigaction(SIGSEGV, nullptr, &check);
+  if (memcmp(&action, &check, sizeof(action)) != 0) {
+    printf("sigaction returned different value\n");
+  }
+  signal(BLOCKED_SIGNAL, blocked_signal);
+  signal(UNBLOCKED_SIGNAL, unblocked_signal);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_terminateSignalTest(JNIEnv*, jclass) {
@@ -96,6 +128,12 @@
 char *go_away_compiler = nullptr;
 
 extern "C" JNIEXPORT jint JNICALL Java_Main_testSignal(JNIEnv*, jclass) {
+  // Unblock UNBLOCKED_SIGNAL.
+  sigset_t mask;
+  memset(&mask, 0, sizeof(mask));
+  sigaddset(&mask, UNBLOCKED_SIGNAL);
+  sigprocmask(SIG_UNBLOCK, &mask, nullptr);
+
 #if defined(__arm__) || defined(__i386__) || defined(__aarch64__)
   // On supported architectures we cause a real SEGV.
   *go_away_compiler = 'a';
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index 41329af..f913cf6 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -395,20 +395,6 @@
 #endif
 #endif
 
-static bool cannot_be_blocked(int signum) {
-  // These two sigs cannot be blocked anywhere.
-  if ((signum == SIGKILL) || (signum == SIGSTOP)) {
-      return true;
-  }
-
-  // The invalid rt_sig cannot be blocked.
-  if (((signum >= 32) && (signum < SIGRTMIN)) || (signum > SIGRTMAX)) {
-      return true;
-  }
-
-  return false;
-}
-
 // A dummy special handler, continueing after the faulting location. This code comes from
 // 004-SignalTest.
 static bool nb_signalhandler(int sig, siginfo_t* info ATTRIBUTE_UNUSED, void* context) {
@@ -433,22 +419,6 @@
 #endif
   }
 
-  // Before invoking this handler, all other unclaimed signals must be blocked.
-  // We're trying to check the signal mask to verify its status here.
-  sigset_t tmpset;
-  sigemptyset(&tmpset);
-  sigprocmask(SIG_SETMASK, nullptr, &tmpset);
-  int other_claimed = (sig == SIGSEGV) ? SIGILL : SIGSEGV;
-  for (int signum = 0; signum < NSIG; ++signum) {
-    if (cannot_be_blocked(signum)) {
-        continue;
-    } else if ((sigismember(&tmpset, signum)) && (signum == other_claimed)) {
-      printf("ERROR: The claimed signal %d is blocked\n", signum);
-    } else if ((!sigismember(&tmpset, signum)) && (signum != other_claimed)) {
-      printf("ERROR: The unclaimed signal %d is not blocked\n", signum);
-    }
-  }
-
   // We handled this...
   return true;
 }
diff --git a/test/616-cha-abstract/src/Main.java b/test/616-cha-abstract/src/Main.java
index e1d7db1..b33f575 100644
--- a/test/616-cha-abstract/src/Main.java
+++ b/test/616-cha-abstract/src/Main.java
@@ -39,8 +39,8 @@
 }
 
 public class Main {
-  static Main1 sMain1;
-  static Main1 sMain2;
+  static Base sMain1;
+  static Base sMain2;
 
   static boolean sIsOptimizing = true;
   static boolean sHasJIT = true;
diff --git a/test/616-cha-interface-default/expected.txt b/test/616-cha-interface-default/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/616-cha-interface-default/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/616-cha-interface-default/info.txt b/test/616-cha-interface-default/info.txt
new file mode 100644
index 0000000..11baa1f
--- /dev/null
+++ b/test/616-cha-interface-default/info.txt
@@ -0,0 +1,2 @@
+Test for Class Hierarchy Analysis (CHA) on interface method.
+Test it under multidex configuration to check cross-dex inlining.
diff --git a/test/616-cha-interface-default/multidex.jpp b/test/616-cha-interface-default/multidex.jpp
new file mode 100644
index 0000000..b0d200e
--- /dev/null
+++ b/test/616-cha-interface-default/multidex.jpp
@@ -0,0 +1,3 @@
+Main:
+  @@com.android.jack.annotations.ForceInMainDex
+  class Main
diff --git a/test/616-cha-interface-default/run b/test/616-cha-interface-default/run
new file mode 100644
index 0000000..d8b4f0d
--- /dev/null
+++ b/test/616-cha-interface-default/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Run without an app image to prevent the classes to be loaded at startup.
+exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-interface-default/src-multidex/Base.java b/test/616-cha-interface-default/src-multidex/Base.java
new file mode 100644
index 0000000..2cbcb50
--- /dev/null
+++ b/test/616-cha-interface-default/src-multidex/Base.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Base {
+  default public int foo(int i) {
+    if (i != 1) {
+      return -2;
+    }
+    return i + 10;
+  }
+
+  // Test default method that's not inlined.
+  default public int $noinline$bar() {
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    return -1;
+  }
+
+  default void printError(String msg) {
+    System.out.println(msg);
+  }
+}
diff --git a/test/616-cha-interface-default/src/Main.java b/test/616-cha-interface-default/src/Main.java
new file mode 100644
index 0000000..951607d
--- /dev/null
+++ b/test/616-cha-interface-default/src/Main.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Main1 implements Base {
+}
+
+class Main2 extends Main1 {
+  public void foobar() {}
+}
+
+class Main3 implements Base {
+  public int foo(int i) {
+    if (i != 3) {
+      printError("error3");
+    }
+    return -(i + 10);
+  }
+}
+
+public class Main {
+  static Base sMain1;
+  static Base sMain2;
+  static Base sMain3;
+
+  static boolean sIsOptimizing = true;
+  static boolean sHasJIT = true;
+  static volatile boolean sOtherThreadStarted;
+
+  private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+    if (hasSingleImplementation(clazz, method_name) != b) {
+      System.out.println(clazz + "." + method_name +
+          " doesn't have single implementation value of " + b);
+    }
+  }
+
+  static int getValue(Class<?> cls) {
+    if (cls == Main1.class || cls == Main2.class) {
+      return 1;
+    }
+    return 3;
+  }
+
+  // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked.
+  // So sMain1.foo() can be devirtualized to Base.foo() and be inlined.
+  // After Dummy.createMain3() which links in Main3, live testImplement() on stack
+  // should be deoptimized.
+  static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) {
+    if (setHasJIT) {
+      if (isInterpreted()) {
+        sHasJIT = false;
+      }
+      return;
+    }
+
+    if (createMain3 && (sIsOptimizing || sHasJIT)) {
+      assertIsManaged();
+    }
+
+    if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
+      System.out.println("11 expected.");
+    }
+    if (sMain1.$noinline$bar() != -1) {
+      System.out.println("-1 expected.");
+    }
+    if (sMain2.foo(getValue(sMain2.getClass())) != 11) {
+      System.out.println("11 expected.");
+    }
+
+    if (createMain3) {
+      // Wait for the other thread to start.
+      while (!sOtherThreadStarted);
+      // Create an Main2 instance and assign it to sMain2.
+      // sMain1 is kept the same.
+      sMain3 = Dummy.createMain3();
+      // Wake up the other thread.
+      synchronized(Main.class) {
+        Main.class.notify();
+      }
+    } else if (wait) {
+      // This is the other thread.
+      synchronized(Main.class) {
+        sOtherThreadStarted = true;
+        // Wait for Main2 to be linked and deoptimization is triggered.
+        try {
+          Main.class.wait();
+        } catch (Exception e) {
+        }
+      }
+    }
+
+    // There should be a deoptimization here right after Main3 is linked by
+    // calling Dummy.createMain3(), even though sMain1 didn't change.
+    // The behavior here would be different if inline-cache is used, which
+    // doesn't deoptimize since sMain1 still hits the type cache.
+    if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
+      System.out.println("11 expected.");
+    }
+    if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) {
+      // This method should be deoptimized right after Main3 is created.
+      assertIsInterpreted();
+    }
+
+    if (sMain3 != null) {
+      if (sMain3.foo(getValue(sMain3.getClass())) != -13) {
+        System.out.println("-13 expected.");
+      }
+    }
+  }
+
+  // Test scenarios under which CHA-based devirtualization happens,
+  // and class loading that implements a method can invalidate compiled code.
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+
+    if (isInterpreted()) {
+      sIsOptimizing = false;
+    }
+
+    // sMain1 is an instance of Main1.
+    // sMain2 is an instance of Main2.
+    // Neither Main1 nor Main2 override default method Base.foo().
+    // Main3 hasn't bee loaded yet.
+    sMain1 = new Main1();
+    sMain2 = new Main2();
+
+    ensureJitCompiled(Main.class, "testImplement");
+    testImplement(false, false, true);
+
+    if (sHasJIT && !sIsOptimizing) {
+      assertSingleImplementation(Base.class, "foo", true);
+      assertSingleImplementation(Main1.class, "foo", true);
+    } else {
+      // Main3 is verified ahead-of-time so it's linked in already.
+    }
+
+    // Create another thread that also calls sMain1.foo().
+    // Try to test suspend and deopt another thread.
+    new Thread() {
+      public void run() {
+        testImplement(false, true, false);
+      }
+    }.start();
+
+    // This will create Main3 instance in the middle of testImplement().
+    testImplement(true, false, false);
+    assertSingleImplementation(Base.class, "foo", false);
+    assertSingleImplementation(Main1.class, "foo", true);
+    assertSingleImplementation(sMain3.getClass(), "foo", true);
+  }
+
+  private static native void ensureJitCompiled(Class<?> itf, String method_name);
+  private static native void assertIsInterpreted();
+  private static native void assertIsManaged();
+  private static native boolean isInterpreted();
+  private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+}
+
+// Put createMain3() in another class to avoid class loading due to verifier.
+class Dummy {
+  static Base createMain3() {
+    return new Main3();
+  }
+}
diff --git a/test/616-cha-interface/expected.txt b/test/616-cha-interface/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/616-cha-interface/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/616-cha-interface/info.txt b/test/616-cha-interface/info.txt
new file mode 100644
index 0000000..1fd330a
--- /dev/null
+++ b/test/616-cha-interface/info.txt
@@ -0,0 +1 @@
+Test for Class Hierarchy Analysis (CHA) on interface method.
diff --git a/test/616-cha-interface/run b/test/616-cha-interface/run
new file mode 100644
index 0000000..d8b4f0d
--- /dev/null
+++ b/test/616-cha-interface/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Run without an app image to prevent the classes to be loaded at startup.
+exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-interface/src/Main.java b/test/616-cha-interface/src/Main.java
new file mode 100644
index 0000000..3c93496
--- /dev/null
+++ b/test/616-cha-interface/src/Main.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Base {
+  void foo(int i);
+  void $noinline$bar();
+}
+
+class Main1 implements Base {
+  public void foo(int i) {
+    if (i != 1) {
+      printError("error1");
+    }
+  }
+
+  // Test rewriting invoke-interface into invoke-virtual when inlining fails.
+  public void $noinline$bar() {
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+    System.out.print("");
+  }
+
+  void printError(String msg) {
+    System.out.println(msg);
+  }
+}
+
+class Main2 extends Main1 {
+  public void foo(int i) {
+    if (i != 2) {
+      printError("error2");
+    }
+  }
+}
+
+public class Main {
+  static Base sMain1;
+  static Base sMain2;
+
+  static boolean sIsOptimizing = true;
+  static boolean sHasJIT = true;
+  static volatile boolean sOtherThreadStarted;
+
+  private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+    if (hasSingleImplementation(clazz, method_name) != b) {
+      System.out.println(clazz + "." + method_name +
+          " doesn't have single implementation value of " + b);
+    }
+  }
+
+  // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+  // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+  // After Dummy.createMain2() which links in Main2, live testImplement() on stack
+  // should be deoptimized.
+  static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) {
+    if (setHasJIT) {
+      if (isInterpreted()) {
+        sHasJIT = false;
+      }
+      return;
+    }
+
+    if (createMain2 && (sIsOptimizing || sHasJIT)) {
+      assertIsManaged();
+    }
+
+    sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+    sMain1.$noinline$bar();
+
+    if (createMain2) {
+      // Wait for the other thread to start.
+      while (!sOtherThreadStarted);
+      // Create an Main2 instance and assign it to sMain2.
+      // sMain1 is kept the same.
+      sMain2 = Dummy.createMain2();
+      // Wake up the other thread.
+      synchronized(Main.class) {
+        Main.class.notify();
+      }
+    } else if (wait) {
+      // This is the other thread.
+      synchronized(Main.class) {
+        sOtherThreadStarted = true;
+        // Wait for Main2 to be linked and deoptimization is triggered.
+        try {
+          Main.class.wait();
+        } catch (Exception e) {
+        }
+      }
+    }
+
+    // There should be a deoptimization here right after Main2 is linked by
+    // calling Dummy.createMain2(), even though sMain1 didn't change.
+    // The behavior here would be different if inline-cache is used, which
+    // doesn't deoptimize since sMain1 still hits the type cache.
+    sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+    if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+      // This method should be deoptimized right after Main2 is created.
+      assertIsInterpreted();
+    }
+
+    if (sMain2 != null) {
+      sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+    }
+  }
+
+  // Test scenarios under which CHA-based devirtualization happens,
+  // and class loading that overrides a method can invalidate compiled code.
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+
+    if (isInterpreted()) {
+      sIsOptimizing = false;
+    }
+
+    // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+    sMain1 = new Main1();
+
+    ensureJitCompiled(Main.class, "testImplement");
+    testImplement(false, false, true);
+
+    if (sHasJIT && !sIsOptimizing) {
+      assertSingleImplementation(Base.class, "foo", true);
+      assertSingleImplementation(Main1.class, "foo", true);
+    } else {
+      // Main2 is verified ahead-of-time so it's linked in already.
+    }
+
+    // Create another thread that also calls sMain1.foo().
+    // Try to test suspend and deopt another thread.
+    new Thread() {
+      public void run() {
+        testImplement(false, true, false);
+      }
+    }.start();
+
+    // This will create Main2 instance in the middle of testImplement().
+    testImplement(true, false, false);
+    assertSingleImplementation(Base.class, "foo", false);
+    assertSingleImplementation(Main1.class, "foo", false);
+  }
+
+  private static native void ensureJitCompiled(Class<?> itf, String method_name);
+  private static native void assertIsInterpreted();
+  private static native void assertIsManaged();
+  private static native boolean isInterpreted();
+  private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+}
+
+// Put createMain2() in another class to avoid class loading due to verifier.
+class Dummy {
+  static Main1 createMain2() {
+    return new Main2();
+  }
+}
diff --git a/test/616-cha-miranda/expected.txt b/test/616-cha-miranda/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/616-cha-miranda/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/616-cha-miranda/info.txt b/test/616-cha-miranda/info.txt
new file mode 100644
index 0000000..c46f33f
--- /dev/null
+++ b/test/616-cha-miranda/info.txt
@@ -0,0 +1 @@
+Test for Class Hierarchy Analysis (CHA) on miranda method.
diff --git a/test/616-cha-miranda/run b/test/616-cha-miranda/run
new file mode 100644
index 0000000..d8b4f0d
--- /dev/null
+++ b/test/616-cha-miranda/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Run without an app image to prevent the classes to be loaded at startup.
+exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-miranda/src/Main.java b/test/616-cha-miranda/src/Main.java
new file mode 100644
index 0000000..e548482
--- /dev/null
+++ b/test/616-cha-miranda/src/Main.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Iface {
+  public void foo(int i);
+}
+
+abstract class Base implements Iface {
+  // Iface.foo(int) will be added as a miranda method.
+
+  void printError(String msg) {
+    System.out.println(msg);
+  }
+}
+
+class Main1 extends Base {
+  public void foo(int i) {
+    if (i != 1) {
+      printError("error1");
+    }
+  }
+}
+
+class Main2 extends Main1 {
+  public void foo(int i) {
+    if (i != 2) {
+      printError("error2");
+    }
+  }
+}
+
+public class Main {
+  static Base sMain1;
+  static Base sMain2;
+
+  static boolean sIsOptimizing = true;
+  static boolean sHasJIT = true;
+  static volatile boolean sOtherThreadStarted;
+
+  private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+    if (hasSingleImplementation(clazz, method_name) != b) {
+      System.out.println(clazz + "." + method_name +
+          " doesn't have single implementation value of " + b);
+    }
+  }
+
+  // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+  // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+  // After Dummy.createMain2() which links in Main2, live testOverride() on stack
+  // should be deoptimized.
+  static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
+    if (setHasJIT) {
+      if (isInterpreted()) {
+        sHasJIT = false;
+      }
+      return;
+    }
+
+    if (createMain2 && (sIsOptimizing || sHasJIT)) {
+      assertIsManaged();
+    }
+
+    sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+    if (createMain2) {
+      // Wait for the other thread to start.
+      while (!sOtherThreadStarted);
+      // Create an Main2 instance and assign it to sMain2.
+      // sMain1 is kept the same.
+      sMain2 = Dummy.createMain2();
+      // Wake up the other thread.
+      synchronized(Main.class) {
+        Main.class.notify();
+      }
+    } else if (wait) {
+      // This is the other thread.
+      synchronized(Main.class) {
+        sOtherThreadStarted = true;
+        // Wait for Main2 to be linked and deoptimization is triggered.
+        try {
+          Main.class.wait();
+        } catch (Exception e) {
+        }
+      }
+    }
+
+    // There should be a deoptimization here right after Main2 is linked by
+    // calling Dummy.createMain2(), even though sMain1 didn't change.
+    // The behavior here would be different if inline-cache is used, which
+    // doesn't deoptimize since sMain1 still hits the type cache.
+    sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+    if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+      // This method should be deoptimized right after Main2 is created.
+      assertIsInterpreted();
+    }
+
+    if (sMain2 != null) {
+      sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+    }
+  }
+
+  // Test scenarios under which CHA-based devirtualization happens,
+  // and class loading that overrides a method can invalidate compiled code.
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+
+    if (isInterpreted()) {
+      sIsOptimizing = false;
+    }
+
+    // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+    sMain1 = new Main1();
+
+    ensureJitCompiled(Main.class, "testOverride");
+    testOverride(false, false, true);
+
+    if (sHasJIT && !sIsOptimizing) {
+      assertSingleImplementation(Base.class, "foo", true);
+      assertSingleImplementation(Main1.class, "foo", true);
+    } else {
+      // Main2 is verified ahead-of-time so it's linked in already.
+    }
+
+    // Create another thread that also calls sMain1.foo().
+    // Try to test suspend and deopt another thread.
+    new Thread() {
+      public void run() {
+        testOverride(false, true, false);
+      }
+    }.start();
+
+    // This will create Main2 instance in the middle of testOverride().
+    testOverride(true, false, false);
+    assertSingleImplementation(Base.class, "foo", false);
+    assertSingleImplementation(Main1.class, "foo", false);
+  }
+
+  private static native void ensureJitCompiled(Class<?> itf, String method_name);
+  private static native void assertIsInterpreted();
+  private static native void assertIsManaged();
+  private static native boolean isInterpreted();
+  private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+}
+
+// Put createMain2() in another class to avoid class loading due to verifier.
+class Dummy {
+  static Main1 createMain2() {
+    return new Main2();
+  }
+}
diff --git a/test/616-cha-proxy-method-inline/expected.txt b/test/616-cha-proxy-method-inline/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/616-cha-proxy-method-inline/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/616-cha-proxy-method-inline/info.txt b/test/616-cha-proxy-method-inline/info.txt
new file mode 100644
index 0000000..0126855
--- /dev/null
+++ b/test/616-cha-proxy-method-inline/info.txt
@@ -0,0 +1 @@
+Test for Class Hierarchy Analysis (CHA) on inlining a cross-dex proxy method.
diff --git a/test/616-cha-proxy-method-inline/multidex.jpp b/test/616-cha-proxy-method-inline/multidex.jpp
new file mode 100644
index 0000000..b0d200e
--- /dev/null
+++ b/test/616-cha-proxy-method-inline/multidex.jpp
@@ -0,0 +1,3 @@
+Main:
+  @@com.android.jack.annotations.ForceInMainDex
+  class Main
diff --git a/test/616-cha-proxy-method-inline/run b/test/616-cha-proxy-method-inline/run
new file mode 100644
index 0000000..d8b4f0d
--- /dev/null
+++ b/test/616-cha-proxy-method-inline/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Run without an app image to prevent the classes to be loaded at startup.
+exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-proxy-method-inline/src-multidex/Foo.java b/test/616-cha-proxy-method-inline/src-multidex/Foo.java
new file mode 100644
index 0000000..9deca3e
--- /dev/null
+++ b/test/616-cha-proxy-method-inline/src-multidex/Foo.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Foo {
+  public Object bar(Object obj);
+}
diff --git a/test/616-cha-proxy-method-inline/src/Main.java b/test/616-cha-proxy-method-inline/src/Main.java
new file mode 100644
index 0000000..be7bc82
--- /dev/null
+++ b/test/616-cha-proxy-method-inline/src/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+class DebugProxy implements java.lang.reflect.InvocationHandler {
+  private Object obj;
+  static Class<?>[] interfaces = {Foo.class};
+
+  public static Object newInstance(Object obj) {
+    return java.lang.reflect.Proxy.newProxyInstance(
+      Foo.class.getClassLoader(),
+      interfaces,
+      new DebugProxy(obj));
+  }
+
+  private DebugProxy(Object obj) {
+    this.obj = obj;
+  }
+
+  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
+    Object result;
+    if (obj == null) {
+      return null;
+    }
+    try {
+      System.out.println("before invoking method " + m.getName());
+      result = m.invoke(obj, args);
+    } catch (InvocationTargetException e) {
+      throw e.getTargetException();
+    } catch (Exception e) {
+      throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
+    } finally {
+      System.out.println("after invoking method " + m.getName());
+    }
+    return result;
+  }
+}
+
+public class Main {
+  public static void call(Foo foo) {
+    if (foo == null) {
+      return;
+    }
+    foo.bar(null);
+  }
+
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+    Foo foo = (Foo)DebugProxy.newInstance(null);
+    ensureJitCompiled(Main.class, "call");
+    call(foo);
+  }
+
+  private static native void ensureJitCompiled(Class<?> itf, String method_name);
+}
diff --git a/test/644-checker-deopt/expected.txt b/test/644-checker-deopt/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/644-checker-deopt/expected.txt
diff --git a/test/644-checker-deopt/info.txt b/test/644-checker-deopt/info.txt
new file mode 100644
index 0000000..c5fb12c
--- /dev/null
+++ b/test/644-checker-deopt/info.txt
@@ -0,0 +1,2 @@
+Regression test for making sure HDeoptimize is executed before
+the code it should have prevented executing.
diff --git a/test/644-checker-deopt/profile b/test/644-checker-deopt/profile
new file mode 100644
index 0000000..cb261cc
--- /dev/null
+++ b/test/644-checker-deopt/profile
@@ -0,0 +1,2 @@
+LMain;->inlineMonomorphic(LMain;)I+LMain;
+LMain;->inlinePolymorphic(LMain;)I+LMain;,LSubMain;
diff --git a/test/644-checker-deopt/run b/test/644-checker-deopt/run
new file mode 100644
index 0000000..146e180
--- /dev/null
+++ b/test/644-checker-deopt/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/644-checker-deopt/src/Main.java b/test/644-checker-deopt/src/Main.java
new file mode 100644
index 0000000..17c80a6
--- /dev/null
+++ b/test/644-checker-deopt/src/Main.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  /// CHECK-START: int Main.inlineMonomorphic(Main) inliner (before)
+  /// CHECK:       InvokeVirtual method_name:Main.getValue
+
+  /// CHECK-START: int Main.inlineMonomorphic(Main) inliner (after)
+  /// CHECK-NOT:   InvokeVirtual method_name:Main.getValue
+
+  /// CHECK-START: int Main.inlineMonomorphic(Main) licm (before)
+  /// CHECK:   <<Deopt:l\d+>> Deoptimize
+  /// CHECK:                  InstanceFieldGet [<<Deopt>>] field_name:Main.value
+
+  /// CHECK-START: int Main.inlineMonomorphic(Main) licm (after)
+  /// CHECK:   <<Deopt:l\d+>> Deoptimize
+  /// CHECK:                  InstanceFieldGet [<<Deopt>>] field_name:Main.value
+
+  public static int inlineMonomorphic(Main a) {
+    if (a == null) {
+      return 42;
+    }
+    int i = 0;
+    while (i < 100) {
+      i += a.getValue();
+    }
+    return i;
+  }
+
+  /// CHECK-START: int Main.inlinePolymorphic(Main) inliner (before)
+  /// CHECK:       InvokeVirtual method_name:Main.getValue
+
+  /// CHECK-START: int Main.inlinePolymorphic(Main) inliner (after)
+  /// CHECK-NOT:   InvokeVirtual method_name:Main.getValue
+
+  /// CHECK-START: int Main.inlineMonomorphic(Main) licm (before)
+  /// CHECK:   <<Deopt:l\d+>> Deoptimize
+  /// CHECK:                  InstanceFieldGet [<<Deopt>>] field_name:Main.value
+
+  /// CHECK-START: int Main.inlineMonomorphic(Main) licm (after)
+  /// CHECK:   <<Deopt:l\d+>> Deoptimize
+  /// CHECK:                  InstanceFieldGet [<<Deopt>>] field_name:Main.value
+  public static int inlinePolymorphic(Main a) {
+    return a.getValue();
+  }
+
+  public int getValue() {
+    return value;
+  }
+
+  public static void main(String[] args) {
+    inlineMonomorphic(new Main());
+  }
+
+  int value = 1;
+}
+
+// Add a subclass of 'Main' to write the polymorphic inline cache in the profile.
+class SubMain extends Main {
+}
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc
index 9166277..cbd7686 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -16,6 +16,8 @@
 
 #include "901-hello-ti-agent/basics.h"
 
+#include <thread>
+
 #include <jni.h>
 #include <stdio.h>
 #include <string.h>
@@ -159,5 +161,19 @@
   return (current_phase == JVMTI_PHASE_LIVE) ? JNI_TRUE : JNI_FALSE;
 }
 
+static void CallJvmtiFunction(jvmtiEnv* env, jclass klass, jvmtiError* err) {
+  jint n;
+  jmethodID* methods = nullptr;
+  *err = env->GetClassMethods(klass, &n, &methods);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkUnattached(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass) {
+  jvmtiError res = JVMTI_ERROR_NONE;
+  std::thread t1(CallJvmtiFunction, jvmti_env, Main_klass, &res);
+  t1.join();
+  return res == JVMTI_ERROR_UNATTACHED_THREAD;
+}
+
 }  // namespace Test901HelloTi
 }  // namespace art
diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt
index c4b24cb..eb5b6a2 100644
--- a/test/901-hello-ti-agent/expected.txt
+++ b/test/901-hello-ti-agent/expected.txt
@@ -3,6 +3,7 @@
 VMInit
 Hello, world!
 Agent in live phase.
+Received expected error for unattached JVMTI calls
 0
 1
 2
diff --git a/test/901-hello-ti-agent/src/Main.java b/test/901-hello-ti-agent/src/Main.java
index 4d62ed3..556e05b 100644
--- a/test/901-hello-ti-agent/src/Main.java
+++ b/test/901-hello-ti-agent/src/Main.java
@@ -21,6 +21,9 @@
     if (checkLivePhase()) {
       System.out.println("Agent in live phase.");
     }
+    if (checkUnattached()) {
+      System.out.println("Received expected error for unattached JVMTI calls");
+    }
 
     set(0);  // OTHER
     set(1);  // GC
@@ -41,4 +44,5 @@
 
   private static native boolean checkLivePhase();
   private static native void setVerboseFlag(int flag, boolean value);
+  private static native boolean checkUnattached();
 }
diff --git a/test/931-agent-thread/agent_thread.cc b/test/931-agent-thread/agent_thread.cc
index f8f9e48..2e6bd46 100644
--- a/test/931-agent-thread/agent_thread.cc
+++ b/test/931-agent-thread/agent_thread.cc
@@ -36,7 +36,8 @@
 struct AgentData {
   AgentData() : main_thread(nullptr),
                 jvmti_env(nullptr),
-                b(2) {
+                b(2),
+                priority(0) {
   }
 
   jthread main_thread;
diff --git a/test/981-dedup-original-dex/expected.txt b/test/981-dedup-original-dex/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/981-dedup-original-dex/expected.txt
diff --git a/test/981-dedup-original-dex/info.txt b/test/981-dedup-original-dex/info.txt
new file mode 100644
index 0000000..62696e0
--- /dev/null
+++ b/test/981-dedup-original-dex/info.txt
@@ -0,0 +1,4 @@
+Tests basic functions in the jvmti plugin.
+
+This checks that we do not needlessly duplicate the contents of retransformed
+classes original dex files.
diff --git a/test/981-dedup-original-dex/run b/test/981-dedup-original-dex/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/981-dedup-original-dex/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/981-dedup-original-dex/src/Main.java b/test/981-dedup-original-dex/src/Main.java
new file mode 100644
index 0000000..cd3f007
--- /dev/null
+++ b/test/981-dedup-original-dex/src/Main.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+import java.util.Base64;
+
+import dalvik.system.ClassExt;
+
+public class Main {
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static final byte[] DEX_BYTES_1 = Base64.getDecoder().decode(
+    "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
+    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform2 {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye2");
+   *   }
+   * }
+   */
+  private static final byte[] DEX_BYTES_2 = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAjXDED2iflQ3NXbPtBRVjQVMqoDU9nDz/QAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
+    "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" +
+    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" +
+    "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+    "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTIADExUcmFuc2Zvcm0yOwAVTGphdmEvaW8vUHJp" +
+    "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" +
+    "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMzAA" +
+    "A291dAAHcHJpbnRsbgAFc2F5SGkAAQAHDgADAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" +
+    "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
+    "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
+    "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA");
+
+  public static void main(String[] args) {
+    try {
+      doTest();
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  private static void assertSame(Object a, Object b) throws Exception {
+    if (a != b) {
+      throw new AssertionError("'" + (a != null ? a.toString() : "null") + "' is not the same as " +
+                               "'" + (b != null ? b.toString() : "null") + "'");
+    }
+  }
+
+  private static Object getObjectField(Object o, String name) throws Exception {
+    return getObjectField(o, o.getClass(), name);
+  }
+
+  private static Object getObjectField(Object o, Class<?> type, String name) throws Exception {
+    Field f = type.getDeclaredField(name);
+    f.setAccessible(true);
+    return f.get(o);
+  }
+
+  private static Object getOriginalDexFile(Class<?> k) throws Exception {
+    ClassExt ext_data_object = (ClassExt) getObjectField(k, "extData");
+    if (ext_data_object == null) {
+      return null;
+    }
+
+    return getObjectField(ext_data_object, "originalDexFile");
+  }
+
+  public static void doTest() throws Exception {
+    // Make sure both of these are loaded prior to transformations being added so they have the same
+    // original dex files.
+    Transform t1 = new Transform();
+    Transform2 t2 = new Transform2();
+
+    assertSame(null, getOriginalDexFile(t1.getClass()));
+    assertSame(null, getOriginalDexFile(t2.getClass()));
+    assertSame(null, getOriginalDexFile(Main.class));
+
+    addCommonTransformationResult("Transform", new byte[0], DEX_BYTES_1);
+    addCommonTransformationResult("Transform2", new byte[0], DEX_BYTES_2);
+    enableCommonRetransformation(true);
+    doCommonClassRetransformation(Transform.class, Transform2.class);
+
+    assertSame(getOriginalDexFile(t1.getClass()), getOriginalDexFile(t2.getClass()));
+    assertSame(null, getOriginalDexFile(Main.class));
+    // Make sure that the original dex file is a DexCache object.
+    assertSame(getOriginalDexFile(t1.getClass()).getClass(), Class.forName("java.lang.DexCache"));
+
+    // Check that we end up with a byte[] if we do a direct RedefineClasses
+    enableCommonRetransformation(false);
+    doCommonClassRedefinition(Transform.class, new byte[0], DEX_BYTES_1);
+    assertSame((new byte[0]).getClass(), getOriginalDexFile(t1.getClass()).getClass());
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRetransformation(Class<?>... target);
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] class_file,
+                                                       byte[] dex_file);
+  private static native void enableCommonRetransformation(boolean enable);
+  private static native void addCommonTransformationResult(String target_name,
+                                                           byte[] class_bytes,
+                                                           byte[] dex_bytes);
+}
diff --git a/test/981-dedup-original-dex/src/Transform.java b/test/981-dedup-original-dex/src/Transform.java
new file mode 100644
index 0000000..3c97907
--- /dev/null
+++ b/test/981-dedup-original-dex/src/Transform.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform {
+  public void sayHi() {
+    System.out.println("hello");
+  }
+}
diff --git a/test/981-dedup-original-dex/src/Transform2.java b/test/981-dedup-original-dex/src/Transform2.java
new file mode 100644
index 0000000..eb22842
--- /dev/null
+++ b/test/981-dedup-original-dex/src/Transform2.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform2 {
+  public void sayHi() {
+    System.out.println("hello2");
+  }
+}
diff --git a/test/982-ok-no-retransform/expected.txt b/test/982-ok-no-retransform/expected.txt
new file mode 100644
index 0000000..317e967
--- /dev/null
+++ b/test/982-ok-no-retransform/expected.txt
@@ -0,0 +1,2 @@
+hello
+hello
diff --git a/test/982-ok-no-retransform/info.txt b/test/982-ok-no-retransform/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/982-ok-no-retransform/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/982-ok-no-retransform/run b/test/982-ok-no-retransform/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/982-ok-no-retransform/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/982-ok-no-retransform/src/Main.java b/test/982-ok-no-retransform/src/Main.java
new file mode 100644
index 0000000..7bb4a46
--- /dev/null
+++ b/test/982-ok-no-retransform/src/Main.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+public class Main {
+
+  public static void main(String[] args) {
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi();
+    enableCommonRetransformation(true);
+    doCommonClassRetransformation(Transform.class);
+    t.sayHi();
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRetransformation(Class<?>... target);
+  private static native void enableCommonRetransformation(boolean enable);
+}
diff --git a/test/982-ok-no-retransform/src/Transform.java b/test/982-ok-no-retransform/src/Transform.java
new file mode 100644
index 0000000..8e8af35
--- /dev/null
+++ b/test/982-ok-no-retransform/src/Transform.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform {
+  public void sayHi() {
+    // Use lower 'h' to make sure the string will have a different string id
+    // than the transformation (the transformation code is the same except
+    // the actual printed String, which was making the test inacurately passing
+    // in JIT mode when loading the string from the dex cache, as the string ids
+    // of the two different strings were the same).
+    // We know the string ids will be different because lexicographically:
+    // "Goodbye" < "LTransform;" < "hello".
+    System.out.println("hello");
+  }
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 703b911..cc015b0 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -138,6 +138,7 @@
 # specific version depending on the compiler.
 ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
   $(ART_HOST_EXECUTABLES) \
+  $(HOST_OUT_EXECUTABLES)/hprof-conv \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagent) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagentd) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libartagent) \
@@ -177,8 +178,6 @@
 # Required for dx, jasmin, smali, dexmerger, jack.
 host_prereq_rules += $(TEST_ART_RUN_TEST_DEPENDENCIES)
 
-host_prereq_rules += $(HOST_OUT_EXECUTABLES)/hprof-conv
-
 # Classpath for Jack compilation for target.
 target_prereq_rules := $(TARGET_JACK_CLASSPATH_DEPENDENCIES)
 
@@ -225,18 +224,6 @@
 test-art-target-run-test-dependencies : $(target_prereq_rules)
 test-art-run-test-dependencies : test-art-host-run-test-dependencies test-art-target-run-test-dependencies
 
-# Generate list of dependencies required for given target - HOST or TARGET, IMAGE_TYPE,
-# COMPILER_TYPE and ADDRESS_SIZE.
-$(foreach target, $(TARGET_TYPES), \
-  $(foreach image, $(IMAGE_TYPES), \
-    $(foreach compiler, $(COMPILER_TYPES), \
-      $(foreach address_size, $(ALL_ADDRESS_SIZES), $(eval \
-        $(call core-image-dependencies,$(target),$(image),$(compiler),$(address_size)))))))
-
-test-art-host-run-test-dependencies : $(host_prereq_rules)
-test-art-target-run-test-dependencies : $(target_prereq_rules)
-test-art-run-test-dependencies : test-art-host-run-test-dependencies test-art-target-run-test-dependencies
-
 # Create a rule to build and run a test group of the following form:
 # test-art-{1: host target}-run-test
 define define-test-art-host-or-target-run-test-group
@@ -259,8 +246,6 @@
 target_prereq_rules :=
 core-image-dependencies :=
 name-to-var :=
-ART_TEST_HOST_RUN_TEST_DEPENDENCIES :=
-TEST_ART_TARGET_SYNC_DEPS :=
 define-test-art-host-or-target-run-test-group :=
 TARGET_TYPES :=
 COMPILER_TYPES :=
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 2de34ca..8aa0c55 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -332,8 +332,12 @@
     {
         "tests": ["912-classes",
                   "616-cha",
-                  "616-cha-abstract"],
-        "bug": "http://b/36344364 http://b36344221",
+                  "616-cha-abstract",
+                  "616-cha-interface",
+                  "616-cha-interface-default",
+                  "616-cha-miranda",
+                  "616-cha-proxy-method-inline"],
+        "bug": "http://b/36344364 http://b/36344221",
         "variant": "no-dex2oat | relocate-npatchoat"
     },
     {
@@ -358,9 +362,9 @@
         "variant": "interp-ac"
     },
     {
-        "tests": "638-checker-inline-caches",
-        "description": ["Disable 638-checker-inline-caches temporarily until a fix",
-                        "arrives."],
+        "tests": ["638-checker-inline-caches",
+                  "644-checker-deopt"],
+        "description": ["Disabled temporarily until a fix arrives."],
         "bug": "http://b/36371709"
     }
 ]
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 835b678..282ac48 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -51,7 +51,10 @@
   build_command += ' -j' + str(n_threads)
   build_command += ' -C ' + env.ANDROID_BUILD_TOP
   build_command += ' ' + target.get('target')
-  print build_command.split()
+  # Add 'dist' to avoid Jack issues b/36169180.
+  build_command += ' dist'
+  sys.stdout.write(str(build_command))
+  sys.stdout.flush()
   if subprocess.call(build_command.split()):
     sys.exit(1)
 
@@ -64,7 +67,8 @@
   run_test_command += ['--host']
   run_test_command += ['--verbose']
 
-  print run_test_command
+  sys.stdout.write(str(run_test_command))
+  sys.stdout.flush()
   if subprocess.call(run_test_command):
     sys.exit(1)
 
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 3203f7a..149578d 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -53,6 +53,7 @@
 import re
 import subprocess
 import sys
+import tempfile
 import threading
 import time
 
@@ -433,8 +434,10 @@
           options_test += ' --instruction-set-features ' + \
                           env.HOST_2ND_ARCH_PREFIX_DEX2OAT_HOST_INSTRUCTION_SET_FEATURES
 
-      options_test = (' --output-path %s/run-test-output/%s') % (
-        env.ART_HOST_TEST_DIR, test_name) + options_test
+      # TODO(http://36039166): This is a temporary solution to
+      # fix build breakages.
+      options_test = (' --output-path %s') % (
+          tempfile.mkdtemp(dir=env.ART_HOST_TEST_DIR)) + options_test
 
       run_test_sh = env.ANDROID_BUILD_TOP + '/art/test/run-test'
       command = run_test_sh + ' ' + options_test + ' ' + test
@@ -480,7 +483,7 @@
       if test_passed:
         print_test_info(test_name, 'PASS')
       else:
-        failed_tests.append(test_name)
+        failed_tests.append((test_name, script_output))
         if not env.ART_TEST_KEEP_GOING:
           stop_testrunner = True
         print_test_info(test_name, 'FAIL', ('%s\n%s') % (
@@ -491,13 +494,13 @@
     else:
       print_test_info(test_name, '')
   except subprocess.TimeoutExpired as e:
-    failed_tests.append(test_name)
-    print_test_info(test_name, 'TIMEOUT', 'timed out in %d\n%s' % (
+    failed_tests.append((test_name, 'Timed out in %d seconds'))
+    print_test_info(test_name, 'TIMEOUT', 'Timed out in %d seconds\n%s' % (
         timeout, command))
   except Exception as e:
-    failed_tests.append(test_name)
-    print_test_info(test_name, 'FAIL')
-    print_text(('%s\n%s\n\n') % (command, str(e)))
+    failed_tests.append((test_name, str(e)))
+    print_test_info(test_name, 'FAIL',
+    ('%s\n%s\n\n') % (command, str(e)))
   finally:
     semaphore.release()
 
@@ -711,16 +714,16 @@
 
   # Prints the list of skipped tests, if any.
   if skipped_tests:
-    print_text(COLOR_SKIP + 'SKIPPED TESTS' + COLOR_NORMAL + '\n')
+    print_text(COLOR_SKIP + 'SKIPPED TESTS: ' + COLOR_NORMAL + '\n')
     for test in skipped_tests:
       print_text(test + '\n')
     print_text('\n')
 
   # Prints the list of failed tests, if any.
   if failed_tests:
-    print_text(COLOR_ERROR + 'FAILED TESTS' + COLOR_NORMAL + '\n')
-    for test in failed_tests:
-      print_text(test + '\n')
+    print_text(COLOR_ERROR + 'FAILED: ' + COLOR_NORMAL + '\n')
+    for test_info in failed_tests:
+      print_text(('%s\n%s\n' % (test_info[0], test_info[1])))
 
 
 def parse_test_name(test_name):
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index fddae3a..68c7d50 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -121,6 +121,8 @@
   { "943-private-recursive-jit", common_redefine::OnLoad, nullptr },
   { "944-transform-classloaders", common_redefine::OnLoad, nullptr },
   { "945-obsolete-native", common_redefine::OnLoad, nullptr },
+  { "981-dedup-original-dex", common_retransform::OnLoad, nullptr },
+  { "982-ok-no-retransform", common_retransform::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {