Merge "Do a null check on the sibling in the register allocator."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 426c3ca..f67da3f 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -186,6 +186,7 @@
   runtime/base/variant_map_test.cc \
   runtime/base/unix_file/fd_file_test.cc \
   runtime/class_linker_test.cc \
+  runtime/compiler_filter_test.cc \
   runtime/dex_file_test.cc \
   runtime/dex_file_verifier_test.cc \
   runtime/dex_instruction_test.cc \
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index dd24220..7c9ce1e 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -109,7 +109,7 @@
     return false;
   }
   // Don't compile class initializers unless kEverything.
-  if ((compiler_options_->GetCompilerFilter() != CompilerOptions::kEverything) &&
+  if ((compiler_options_->GetCompilerFilter() != CompilerFilter::kEverything) &&
      ((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
     return false;
   }
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index f5969aa..1bd4c3a 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -52,7 +52,7 @@
   // because we don't want to include the PassManagerOptions definition from the header file.
 }
 
-CompilerOptions::CompilerOptions(CompilerFilter compiler_filter,
+CompilerOptions::CompilerOptions(CompilerFilter::Filter compiler_filter,
                                  size_t huge_method_threshold,
                                  size_t large_method_threshold,
                                  size_t small_method_threshold,
@@ -147,25 +147,7 @@
 bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usage) {
   if (option.starts_with("--compiler-filter=")) {
     const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
-    if (strcmp(compiler_filter_string, "verify-none") == 0) {
-      compiler_filter_ = CompilerOptions::kVerifyNone;
-    } else if (strcmp(compiler_filter_string, "interpret-only") == 0) {
-      compiler_filter_ = CompilerOptions::kInterpretOnly;
-    } else if (strcmp(compiler_filter_string, "verify-at-runtime") == 0) {
-      compiler_filter_ = CompilerOptions::kVerifyAtRuntime;
-    } else if (strcmp(compiler_filter_string, "space") == 0) {
-      compiler_filter_ = CompilerOptions::kSpace;
-    } else if (strcmp(compiler_filter_string, "balanced") == 0) {
-      compiler_filter_ = CompilerOptions::kBalanced;
-    } else if (strcmp(compiler_filter_string, "speed") == 0) {
-      compiler_filter_ = CompilerOptions::kSpeed;
-    } else if (strcmp(compiler_filter_string, "everything") == 0) {
-      compiler_filter_ = CompilerOptions::kEverything;
-    } else if (strcmp(compiler_filter_string, "time") == 0) {
-      compiler_filter_ = CompilerOptions::kTime;
-    } else if (strcmp(compiler_filter_string, "verify-profile") == 0) {
-      compiler_filter_ = CompilerOptions::kVerifyProfile;
-    } else {
+    if (!CompilerFilter::ParseCompilerFilter(compiler_filter_string, &compiler_filter_)) {
       Usage("Unknown --compiler-filter value %s", compiler_filter_string);
     }
   } else if (option == "--compile-pic") {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 11a4e06..c67ab6e 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "compiler_filter.h"
 #include "globals.h"
 #include "utils.h"
 
@@ -29,20 +30,8 @@
 
 class CompilerOptions FINAL {
  public:
-  enum CompilerFilter {
-    kVerifyNone,          // Skip verification and compile nothing except JNI stubs.
-    kInterpretOnly,       // Verify, and compile only JNI stubs.
-    kVerifyAtRuntime,     // Only compile JNI stubs and verify at runtime.
-    kSpace,               // Maximize space savings.
-    kBalanced,            // Try to get the best performance return on compilation investment.
-    kSpeed,               // Maximize runtime performance.
-    kEverything,          // Force compilation of everything capable of being compiled.
-    kTime,                // Compile methods, but minimize compilation time.
-    kVerifyProfile,       // Verify only the classes in the profile.
-  };
-
   // Guide heuristics to determine whether to compile method if profile data not available.
-  static const CompilerFilter kDefaultCompilerFilter = kSpeed;
+  static const CompilerFilter::Filter kDefaultCompilerFilter = CompilerFilter::kSpeed;
   static const size_t kDefaultHugeMethodThreshold = 10000;
   static const size_t kDefaultLargeMethodThreshold = 600;
   static const size_t kDefaultSmallMethodThreshold = 60;
@@ -64,7 +53,7 @@
   CompilerOptions();
   ~CompilerOptions();
 
-  CompilerOptions(CompilerFilter compiler_filter,
+  CompilerOptions(CompilerFilter::Filter compiler_filter,
                   size_t huge_method_threshold,
                   size_t large_method_threshold,
                   size_t small_method_threshold,
@@ -88,40 +77,32 @@
                   bool dump_cfg_append,
                   bool force_determinism);
 
-  CompilerFilter GetCompilerFilter() const {
+  CompilerFilter::Filter GetCompilerFilter() const {
     return compiler_filter_;
   }
 
-  void SetCompilerFilter(CompilerFilter compiler_filter) {
+  void SetCompilerFilter(CompilerFilter::Filter compiler_filter) {
     compiler_filter_ = compiler_filter;
   }
 
   bool VerifyAtRuntime() const {
-    return compiler_filter_ == CompilerOptions::kVerifyAtRuntime;
+    return compiler_filter_ == CompilerFilter::kVerifyAtRuntime;
   }
 
   bool IsCompilationEnabled() const {
-    return compiler_filter_ != CompilerOptions::kVerifyNone &&
-           compiler_filter_ != CompilerOptions::kInterpretOnly &&
-           compiler_filter_ != CompilerOptions::kVerifyAtRuntime &&
-           compiler_filter_ != CompilerOptions::kVerifyProfile;
+    return CompilerFilter::IsCompilationEnabled(compiler_filter_);
   }
 
   bool IsVerificationEnabled() const {
-    return compiler_filter_ != CompilerOptions::kVerifyNone &&
-           compiler_filter_ != CompilerOptions::kVerifyAtRuntime;
+    return CompilerFilter::IsVerificationEnabled(compiler_filter_);
   }
 
   bool NeverVerify() const {
-    return compiler_filter_ == CompilerOptions::kVerifyNone;
-  }
-
-  bool IsExtractOnly() const {
-    return compiler_filter_ == CompilerOptions::kVerifyAtRuntime;
+    return compiler_filter_ == CompilerFilter::kVerifyNone;
   }
 
   bool VerifyOnlyProfile() const {
-    return compiler_filter_ == CompilerOptions::kVerifyProfile;
+    return compiler_filter_ == CompilerFilter::kVerifyProfile;
   }
 
   size_t GetHugeMethodThreshold() const {
@@ -271,7 +252,7 @@
   void ParseLargeMethodMax(const StringPiece& option, UsageFn Usage);
   void ParseHugeMethodMax(const StringPiece& option, UsageFn Usage);
 
-  CompilerFilter compiler_filter_;
+  CompilerFilter::Filter compiler_filter_;
   size_t huge_method_threshold_;
   size_t large_method_threshold_;
   size_t small_method_threshold_;
@@ -317,7 +298,6 @@
 
   DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
 };
-std::ostream& operator<<(std::ostream& os, const CompilerOptions::CompilerFilter& rhs);
 
 }  // namespace art
 
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 124afbc..1b62531 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -106,7 +106,6 @@
   HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
   DCHECK(branch_target != nullptr);
   DCHECK(fallthrough_target != nullptr);
-  PotentiallyAddSuspendCheck(branch_target, dex_pc);
   HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
   HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
   T* comparison = new (arena_) T(first, second, dex_pc);
@@ -125,7 +124,6 @@
   HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
   DCHECK(branch_target != nullptr);
   DCHECK(fallthrough_target != nullptr);
-  PotentiallyAddSuspendCheck(branch_target, dex_pc);
   HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
   T* comparison = new (arena_) T(value, graph_->GetIntConstant(0, dex_pc), dex_pc);
   current_block_->AddInstruction(comparison);
@@ -145,8 +143,8 @@
 bool HGraphBuilder::SkipCompilation(const DexFile::CodeItem& code_item,
                                     size_t number_of_branches) {
   const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions();
-  CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter();
-  if (compiler_filter == CompilerOptions::kEverything) {
+  CompilerFilter::Filter compiler_filter = compiler_options.GetCompilerFilter();
+  if (compiler_filter == CompilerFilter::kEverything) {
     return false;
   }
 
@@ -1788,7 +1786,6 @@
                                           int32_t target_offset, uint32_t dex_pc) {
   HBasicBlock* case_target = FindBlockStartingAt(dex_pc + target_offset);
   DCHECK(case_target != nullptr);
-  PotentiallyAddSuspendCheck(case_target, dex_pc);
 
   // The current case's value.
   HInstruction* this_case_value = graph_->GetIntConstant(case_value_int, dex_pc);
@@ -1824,23 +1821,6 @@
   }
 }
 
-void HGraphBuilder::PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc) {
-  int32_t target_offset = target->GetDexPc() - dex_pc;
-  if (target_offset <= 0) {
-    // DX generates back edges to the first encountered return. We can save
-    // time of later passes by not adding redundant suspend checks.
-    HInstruction* last_in_target = target->GetLastInstruction();
-    if (last_in_target != nullptr &&
-        (last_in_target->IsReturn() || last_in_target->IsReturnVoid())) {
-      return;
-    }
-
-    // Add a suspend check to backward branches which may potentially loop. We
-    // can remove them after we recognize loops in the graph.
-    current_block_->AddInstruction(new (arena_) HSuspendCheck(dex_pc));
-  }
-}
-
 bool HGraphBuilder::CanDecodeQuickenedInfo() const {
   return interpreter_metadata_ != nullptr;
 }
@@ -1972,7 +1952,6 @@
       int32_t offset = instruction.GetTargetOffset();
       HBasicBlock* target = FindBlockStartingAt(offset + dex_pc);
       DCHECK(target != nullptr);
-      PotentiallyAddSuspendCheck(target, dex_pc);
       current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
       current_block_->AddSuccessor(target);
       current_block_ = nullptr;
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index e3dd0e82..48f5316 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -133,7 +133,6 @@
   HLocal* GetLocalAt(uint32_t register_index) const;
   void UpdateLocal(uint32_t register_index, HInstruction* instruction, uint32_t dex_pc) const;
   HInstruction* LoadLocal(uint32_t register_index, Primitive::Type type, uint32_t dex_pc) const;
-  void PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc);
   void InitializeParameters(uint16_t number_of_parameters);
 
   // Returns whether the current method needs access check for the type.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 4c9c25f..54793fd 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -3221,7 +3221,7 @@
   if (rhs.IsConstant()) {
     uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
     // Map all rotations to +ve. equivalents on the interval [0,63].
-    rot &= kMaxLongShiftValue;
+    rot &= kMaxLongShiftDistance;
     // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
     // logic below to a simple pair of binary orr.
     // (e.g. 34 bits == in_reg swap + 2 bits right.)
@@ -3374,7 +3374,7 @@
       if (second.IsRegister()) {
         Register second_reg = second.AsRegister<Register>();
         // ARM doesn't mask the shift count so we need to do it ourselves.
-        __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftValue));
+        __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftDistance));
         if (op->IsShl()) {
           __ Lsl(out_reg, first_reg, out_reg);
         } else if (op->IsShr()) {
@@ -3384,7 +3384,7 @@
         }
       } else {
         int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
-        uint32_t shift_value = static_cast<uint32_t>(cst & kMaxIntShiftValue);
+        uint32_t shift_value = cst & kMaxIntShiftDistance;
         if (shift_value == 0) {  // ARM does not support shifting with 0 immediate.
           __ Mov(out_reg, first_reg);
         } else if (op->IsShl()) {
@@ -3410,7 +3410,7 @@
         Register second_reg = second.AsRegister<Register>();
 
         if (op->IsShl()) {
-          __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftValue));
+          __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftDistance));
           // Shift the high part
           __ Lsl(o_h, high, o_l);
           // Shift the low part and `or` what overflew on the high part
@@ -3424,7 +3424,7 @@
           // Shift the low part
           __ Lsl(o_l, low, o_l);
         } else if (op->IsShr()) {
-          __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue));
+          __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance));
           // Shift the low part
           __ Lsr(o_l, low, o_h);
           // Shift the high part and `or` what underflew on the low part
@@ -3438,7 +3438,7 @@
           // Shift the high part
           __ Asr(o_h, high, o_h);
         } else {
-          __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue));
+          __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance));
           // same as Shr except we use `Lsr`s and not `Asr`s
           __ Lsr(o_l, low, o_h);
           __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord));
@@ -3454,7 +3454,7 @@
         DCHECK_NE(o_l, high);
         DCHECK_NE(o_h, low);
         int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
-        uint32_t shift_value = static_cast<uint32_t>(cst & kMaxLongShiftValue);
+        uint32_t shift_value = cst & kMaxLongShiftDistance;
         if (shift_value > 32) {
           if (op->IsShl()) {
             __ Lsl(o_h, low, shift_value - 32);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 088dbb3..46fd852 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1818,9 +1818,8 @@
       Register lhs = InputRegisterAt(instr, 0);
       Operand rhs = InputOperandAt(instr, 1);
       if (rhs.IsImmediate()) {
-        uint32_t shift_value = (type == Primitive::kPrimInt)
-          ? static_cast<uint32_t>(rhs.immediate() & kMaxIntShiftValue)
-          : static_cast<uint32_t>(rhs.immediate() & kMaxLongShiftValue);
+        uint32_t shift_value = rhs.immediate() &
+            (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance);
         if (instr->IsShl()) {
           __ Lsl(dst, lhs, shift_value);
         } else if (instr->IsShr()) {
@@ -1921,9 +1920,8 @@
   // conversion) can have a different type from the current instruction's type,
   // so we manually indicate the type.
   Register right_reg = RegisterFrom(instruction->GetLocations()->InAt(1), type);
-  int64_t shift_amount = (type == Primitive::kPrimInt)
-    ? static_cast<uint32_t>(instruction->GetShiftAmount() & kMaxIntShiftValue)
-    : static_cast<uint32_t>(instruction->GetShiftAmount() & kMaxLongShiftValue);
+  int64_t shift_amount = instruction->GetShiftAmount() &
+      (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance);
 
   Operand right_operand(0);
 
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 4c92063..684d89f 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1455,9 +1455,8 @@
   bool use_imm = rhs_location.IsConstant();
   Register rhs_reg = use_imm ? ZERO : rhs_location.AsRegister<Register>();
   int64_t rhs_imm = use_imm ? CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()) : 0;
-  const uint32_t shift_mask = (type == Primitive::kPrimInt)
-      ? kMaxIntShiftValue
-      : kMaxLongShiftValue;
+  const uint32_t shift_mask =
+      (type == Primitive::kPrimInt) ? kMaxIntShiftDistance : kMaxLongShiftDistance;
   const uint32_t shift_value = rhs_imm & shift_mask;
   // Are the INS (Insert Bit Field) and ROTR instructions supported?
   bool has_ins_rotr = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 955c9a4..0276a07 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1208,9 +1208,8 @@
       }
 
       if (use_imm) {
-        uint32_t shift_value = (type == Primitive::kPrimInt)
-          ? static_cast<uint32_t>(rhs_imm & kMaxIntShiftValue)
-          : static_cast<uint32_t>(rhs_imm & kMaxLongShiftValue);
+        uint32_t shift_value = rhs_imm &
+            (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance);
 
         if (shift_value == 0) {
           if (dst != lhs) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index fb3216c..3123c8b 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3775,7 +3775,7 @@
           __ shrl(first_reg, second_reg);
         }
       } else {
-        int32_t shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue;
+        int32_t shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance;
         if (shift == 0) {
           return;
         }
@@ -3803,7 +3803,7 @@
         }
       } else {
         // Shift by a constant.
-        int shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue;
+        int32_t shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance;
         // Nothing to do if the shift is 0, as the input is already the output.
         if (shift != 0) {
           if (op->IsShl()) {
@@ -3960,7 +3960,7 @@
       Register second_reg = second.AsRegister<Register>();
       __ rorl(first_reg, second_reg);
     } else {
-      Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue);
+      Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance);
       __ rorl(first_reg, imm);
     }
     return;
@@ -3981,7 +3981,7 @@
     __ cmovl(kNotEqual, first_reg_hi, first_reg_lo);
     __ cmovl(kNotEqual, first_reg_lo, temp_reg);
   } else {
-    int32_t shift_amt = CodeGenerator::GetInt64ValueOf(second.GetConstant()) & kMaxLongShiftValue;
+    int32_t shift_amt = second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance;
     if (shift_amt == 0) {
       // Already fine.
       return;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 7648f61..b3b98be 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -3799,7 +3799,7 @@
           __ shrl(first_reg, second_reg);
         }
       } else {
-        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue);
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance);
         if (op->IsShl()) {
           __ shll(first_reg, imm);
         } else if (op->IsShr()) {
@@ -3821,7 +3821,7 @@
           __ shrq(first_reg, second_reg);
         }
       } else {
-        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue);
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance);
         if (op->IsShl()) {
           __ shlq(first_reg, imm);
         } else if (op->IsShr()) {
@@ -3868,7 +3868,7 @@
         CpuRegister second_reg = second.AsRegister<CpuRegister>();
         __ rorl(first_reg, second_reg);
       } else {
-        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue);
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance);
         __ rorl(first_reg, imm);
       }
       break;
@@ -3877,7 +3877,7 @@
         CpuRegister second_reg = second.AsRegister<CpuRegister>();
         __ rorq(first_reg, second_reg);
       } else {
-        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue);
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance);
         __ rorq(first_reg, imm);
       }
       break;
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index 9c69f8c..1e54a0a 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -598,42 +598,41 @@
     "  5: IntConstant [9]\n"            // v1 <- 2
     "  13: IntConstant [14]\n"          // const 5
     "  18: IntConstant [19]\n"          // const 4
-    "  24: IntConstant [25]\n"          // const 8
-    "  30: SuspendCheck\n"
-    "  31: Goto 1\n"
+    "  23: IntConstant [24]\n"          // const 8
+    "  29: SuspendCheck\n"
+    "  30: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 3\n"
     "  9: Add(3, 5) [19]\n"             // v2 <- v0 + v1 = 1 + 2 = 3
     "  11: Goto 3\n"                    // goto L2
     "BasicBlock 2, pred: 3, succ: 4\n"  // L1:
-    "  14: Add(19, 13) [25]\n"          // v1 <- v0 + 3 = 7 + 5 = 12
+    "  14: Add(19, 13) [24]\n"          // v1 <- v0 + 3 = 7 + 5 = 12
     "  16: Goto 4\n"                    // goto L3
     "BasicBlock 3, pred: 1, succ: 2\n"  // L2:
     "  19: Add(9, 18) [14]\n"           // v0 <- v2 + 2 = 3 + 4 = 7
-    "  21: SuspendCheck\n"
-    "  22: Goto 2\n"                    // goto L1
+    "  21: Goto 2\n"                    // goto L1
     "BasicBlock 4, pred: 2, succ: 5\n"  // L3:
-    "  25: Add(14, 24) [28]\n"          // v2 <- v1 + 4 = 12 + 8 = 20
-    "  28: Return(25)\n"                // return v2
+    "  24: Add(14, 23) [27]\n"          // v2 <- v1 + 4 = 12 + 8 = 20
+    "  27: Return(24)\n"                // return v2
     "BasicBlock 5, pred: 4\n"
-    "  29: Exit\n";
+    "  28: Exit\n";
 
   // Expected difference after constant folding.
   diff_t expected_cf_diff = {
     { "  3: IntConstant [9]\n",   "  3: IntConstant\n" },
-    { "  5: IntConstant [9]\n",   "  5: IntConstant []\n" },
+    { "  5: IntConstant [9]\n",   "  5: IntConstant\n" },
     { "  13: IntConstant [14]\n", "  13: IntConstant\n" },
     { "  18: IntConstant [19]\n", "  18: IntConstant\n" },
-    { "  24: IntConstant [25]\n", "  24: IntConstant\n" },
-    { "  30: SuspendCheck\n",     "  30: SuspendCheck\n"
-                                  "  32: IntConstant []\n"
-                                  "  33: IntConstant []\n"
-                                  "  34: IntConstant\n"
-                                  "  35: IntConstant [28]\n" },
+    { "  23: IntConstant [24]\n", "  23: IntConstant\n" },
+    { "  29: SuspendCheck\n",     "  29: SuspendCheck\n"
+                                  "  31: IntConstant\n"
+                                  "  32: IntConstant\n"
+                                  "  33: IntConstant\n"
+                                  "  34: IntConstant [27]\n" },
     { "  9: Add(3, 5) [19]\n",    removed },
-    { "  14: Add(19, 13) [25]\n", removed },
+    { "  14: Add(19, 13) [24]\n", removed },
     { "  19: Add(9, 18) [14]\n",  removed },
-    { "  25: Add(14, 24) [28]\n", removed },
-    { "  28: Return(25)\n",       "  28: Return(35)\n"}
+    { "  24: Add(14, 23) [27]\n", removed },
+    { "  27: Return(24)\n",       "  27: Return(34)\n"}
   };
   std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
 
@@ -656,17 +655,13 @@
   // Expected difference after dead code elimination.
   std::string expected_after_dce =
     "BasicBlock 0, succ: 1\n"
-    "  5: IntConstant []\n"
-    "  30: SuspendCheck\n"
-    "  32: IntConstant []\n"
-    "  33: IntConstant []\n"
-    "  35: IntConstant [28]\n"
-    "  31: Goto 1\n"
+    "  29: SuspendCheck\n"
+    "  34: IntConstant [27]\n"
+    "  30: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 5\n"
-    "  21: SuspendCheck\n"
-    "  28: Return(35)\n"
+    "  27: Return(34)\n"
     "BasicBlock 5, pred: 1\n"
-    "  29: Exit\n";
+    "  28: Exit\n";
 
   TestCode(data,
            expected_before,
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index 930795b..83e724b 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -149,44 +149,32 @@
     "  5: IntConstant [9]\n"
     "  13: IntConstant [14]\n"
     "  18: IntConstant [19]\n"
-    "  24: IntConstant [25]\n"
-    "  29: SuspendCheck\n"
-    "  30: Goto 1\n"
+    "  23: IntConstant [24]\n"
+    "  28: SuspendCheck\n"
+    "  29: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 3\n"
     "  9: Add(3, 5) [19]\n"
     "  11: Goto 3\n"
     "BasicBlock 2, pred: 3, succ: 4\n"
-    "  14: Add(19, 13) [25]\n"
+    "  14: Add(19, 13) [24]\n"
     "  16: Goto 4\n"
     "BasicBlock 3, pred: 1, succ: 2\n"
     "  19: Add(9, 18) [14]\n"
-    "  21: SuspendCheck\n"
-    "  22: Goto 2\n"
+    "  21: Goto 2\n"
     "BasicBlock 4, pred: 2, succ: 5\n"
-    "  25: Add(14, 24)\n"
-    "  27: ReturnVoid\n"
+    "  24: Add(14, 23)\n"
+    "  26: ReturnVoid\n"
     "BasicBlock 5, pred: 4\n"
-    "  28: Exit\n";
+    "  27: Exit\n";
 
-  // The SuspendCheck instruction following this Add instruction
-  // inserts the latter in an environment, thus making it "used" and
-  // therefore non removable.  It ensures that some other Add and
-  // IntConstant instructions cannot be removed, as they are direct
-  // or indirect inputs of the initial Add instruction.
   std::string expected_after =
     "BasicBlock 0, succ: 1\n"
-    "  3: IntConstant [9]\n"
-    "  5: IntConstant [9]\n"
-    "  18: IntConstant [19]\n"
-    "  29: SuspendCheck\n"
-    "  30: Goto 1\n"
+    "  28: SuspendCheck\n"
+    "  29: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 5\n"
-    "  9: Add(3, 5) [19]\n"
-    "  19: Add(9, 18) []\n"
-    "  21: SuspendCheck\n"
-    "  27: ReturnVoid\n"
+    "  26: ReturnVoid\n"
     "BasicBlock 5, pred: 1\n"
-    "  28: Exit\n";
+    "  27: Exit\n";
 
   TestCode(data, expected_before, expected_after);
 }
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 0c22903..528fe44 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -937,9 +937,12 @@
   Primitive::Type lhs_type = op->InputAt(0)->GetType();
   Primitive::Type rhs_type = op->InputAt(1)->GetType();
   Primitive::Type result_type = op->GetType();
+
+  // Type consistency between inputs.
   if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) {
     if (Primitive::PrimitiveKind(rhs_type) != Primitive::kPrimInt) {
-      AddError(StringPrintf("Shift operation %s %d has a non-int kind second input: %s of type %s.",
+      AddError(StringPrintf("Shift/rotate operation %s %d has a non-int kind second input: "
+                            "%s of type %s.",
                             op->DebugName(), op->GetId(),
                             op->InputAt(1)->DebugName(),
                             Primitive::PrettyDescriptor(rhs_type)));
@@ -953,21 +956,38 @@
     }
   }
 
+  // Type consistency between result and input(s).
   if (op->IsCompare()) {
     if (result_type != Primitive::kPrimInt) {
       AddError(StringPrintf("Compare operation %d has a non-int result type: %s.",
                             op->GetId(),
                             Primitive::PrettyDescriptor(result_type)));
     }
-  } else {
-    // Use the first input, so that we can also make this check for shift and rotate operations.
-    if (Primitive::PrimitiveKind(result_type) != Primitive::PrimitiveKind(lhs_type)) {
-      AddError(StringPrintf("Binary operation %s %d has a result kind different "
-                            "from its input kind: %s vs %s.",
+  } else if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) {
+    // Only check the first input (value), as the second one (distance)
+    // must invariably be of kind `int`.
+    if (result_type != Primitive::PrimitiveKind(lhs_type)) {
+      AddError(StringPrintf("Shift/rotate operation %s %d has a result type different "
+                            "from its left-hand side (value) input kind: %s vs %s.",
                             op->DebugName(), op->GetId(),
                             Primitive::PrettyDescriptor(result_type),
                             Primitive::PrettyDescriptor(lhs_type)));
     }
+  } else {
+    if (Primitive::PrimitiveKind(result_type) != Primitive::PrimitiveKind(lhs_type)) {
+      AddError(StringPrintf("Binary operation %s %d has a result kind different "
+                            "from its left-hand side input kind: %s vs %s.",
+                            op->DebugName(), op->GetId(),
+                            Primitive::PrettyDescriptor(result_type),
+                            Primitive::PrettyDescriptor(lhs_type)));
+    }
+    if (Primitive::PrimitiveKind(result_type) != Primitive::PrimitiveKind(rhs_type)) {
+      AddError(StringPrintf("Binary operation %s %d has a result kind different "
+                            "from its right-hand side input kind: %s vs %s.",
+                            op->DebugName(), op->GetId(),
+                            Primitive::PrettyDescriptor(result_type),
+                            Primitive::PrettyDescriptor(rhs_type)));
+    }
   }
 }
 
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4f1e90c..3a9d242 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -382,6 +382,8 @@
   void VisitArraySet(HArraySet* array_set) OVERRIDE {
     StartAttributeStream("value_can_be_null") << std::boolalpha
         << array_set->GetValueCanBeNull() << std::noboolalpha;
+    StartAttributeStream("needs_type_check") << std::boolalpha
+        << array_set->NeedsTypeCheck() << std::noboolalpha;
   }
 
   void VisitCompare(HCompare* compare) OVERRIDE {
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 820c696..5447102 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -56,7 +56,6 @@
   bool TryDeMorganNegationFactoring(HBinaryOperation* op);
   void VisitShift(HBinaryOperation* shift);
 
-  void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE;
   void VisitEqual(HEqual* equal) OVERRIDE;
   void VisitNotEqual(HNotEqual* equal) OVERRIDE;
   void VisitBooleanNot(HBooleanNot* bool_not) OVERRIDE;
@@ -240,8 +239,9 @@
 
   if (input_cst != nullptr) {
     int64_t cst = Int64FromConstant(input_cst);
-    int64_t mask =
-        (input_other->GetType() == Primitive::kPrimLong) ? kMaxLongShiftValue : kMaxIntShiftValue;
+    int64_t mask = (input_other->GetType() == Primitive::kPrimLong)
+        ? kMaxLongShiftDistance
+        : kMaxIntShiftDistance;
     if ((cst & mask) == 0) {
       // Replace code looking like
       //    SHL dst, src, 0
@@ -554,22 +554,6 @@
   }
 }
 
-void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) {
-  HBasicBlock* block = check->GetBlock();
-  // Currently always keep the suspend check at entry.
-  if (block->IsEntryBlock()) return;
-
-  // Currently always keep suspend checks at loop entry.
-  if (block->IsLoopHeader() && block->GetFirstInstruction() == check) {
-    DCHECK(block->GetLoopInformation()->GetSuspendCheck() == check);
-    return;
-  }
-
-  // Remove the suspend check that was added at build time for the baseline
-  // compiler.
-  block->RemoveInstruction(check);
-}
-
 static HCondition* GetOppositeConditionSwapOps(ArenaAllocator* arena, HInstruction* cond) {
   HInstruction *lhs = cond->InputAt(0);
   HInstruction *rhs = cond->InputAt(1);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index c83340b..e08e8fb 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -162,7 +162,7 @@
   // (6) Compute the dominance information and the reverse post order.
   ComputeDominanceInformation();
 
-  // (7) Analyze loops discover through back edge analysis, and
+  // (7) Analyze loops discovered through back edge analysis, and
   //     set the loop information on each block.
   GraphAnalysisResult result = AnalyzeLoops();
   if (result != kAnalysisSuccess) {
@@ -247,7 +247,7 @@
   }
 
   // Populate `dominated_blocks_` information after computing all dominators.
-  // The potential presence of irreducible loops require to do it after.
+  // The potential presence of irreducible loops requires to do it after.
   for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
     if (!block->IsEntryBlock()) {
@@ -460,7 +460,7 @@
     if (block->IsLoopHeader()) {
       SimplifyLoop(block);
     } else if (!block->IsEntryBlock() && block->GetFirstInstruction()->IsSuspendCheck()) {
-      // We are being called by the dead code elimiation pass, and what used to be
+      // We are being called by the dead code elimination pass, and what used to be
       // a loop got dismantled. Just remove the suspend check.
       block->RemoveInstruction(block->GetFirstInstruction());
     }
@@ -2373,7 +2373,7 @@
   env_uses_.Clear();
 }
 
-// Returns an instruction with the opposite boolean value from 'cond'.
+// Returns an instruction with the opposite Boolean value from 'cond'.
 HInstruction* HGraph::InsertOppositeCondition(HInstruction* cond, HInstruction* cursor) {
   ArenaAllocator* allocator = GetArena();
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index a09ad00..521bd14 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -72,8 +72,10 @@
 static const int kDefaultNumberOfDominatedBlocks = 1;
 static const int kDefaultNumberOfBackEdges = 1;
 
-static constexpr uint32_t kMaxIntShiftValue = 0x1f;
-static constexpr uint64_t kMaxLongShiftValue = 0x3f;
+// The maximum (meaningful) distance (31) that can be used in an integer shift/rotate operation.
+static constexpr int32_t kMaxIntShiftDistance = 0x1f;
+// The maximum (meaningful) distance (63) that can be used in a long shift/rotate operation.
+static constexpr int32_t kMaxLongShiftDistance = 0x3f;
 
 static constexpr uint32_t kUnknownFieldIndex = static_cast<uint32_t>(-1);
 static constexpr uint16_t kUnknownClassDefIndex = static_cast<uint16_t>(-1);
@@ -3442,10 +3444,8 @@
                          SideEffectsForArchRuntimeCalls(comparison_type),
                          dex_pc) {
     SetPackedField<ComparisonBiasField>(bias);
-    if (kIsDebugBuild) {
-      DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(first->GetType()));
-      DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(second->GetType()));
-    }
+    DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(first->GetType()));
+    DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(second->GetType()));
   }
 
   template <typename T>
@@ -4447,37 +4447,39 @@
 class HShl : public HBinaryOperation {
  public:
   HShl(Primitive::Type result_type,
-       HInstruction* left,
-       HInstruction* right,
+       HInstruction* value,
+       HInstruction* distance,
        uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
-
-  template <typename T, typename U, typename V>
-  T Compute(T x, U y, V max_shift_value) const {
-    static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value,
-                  "V is not the unsigned integer type corresponding to T");
-    return x << (y & max_shift_value);
+      : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+    DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType()));
+    DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType()));
   }
 
-  HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+  template <typename T>
+  T Compute(T value, int32_t distance, int32_t max_shift_distance) const {
+    return value << (distance & max_shift_distance);
+  }
+
+  HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE {
     return GetBlock()->GetGraph()->GetIntConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
+        Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc());
   }
-  HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
+  HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE {
     return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+        Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc());
   }
-  HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+  HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED,
+                      HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
+    LOG(FATAL) << DebugName() << " is not defined for the (long, long) case.";
+    UNREACHABLE();
   }
-  HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
-                      HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+  HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED,
+                      HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
     LOG(FATAL) << DebugName() << " is not defined for float values";
     UNREACHABLE();
   }
-  HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
-                      HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+  HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED,
+                      HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
     LOG(FATAL) << DebugName() << " is not defined for double values";
     UNREACHABLE();
   }
@@ -4491,37 +4493,39 @@
 class HShr : public HBinaryOperation {
  public:
   HShr(Primitive::Type result_type,
-       HInstruction* left,
-       HInstruction* right,
+       HInstruction* value,
+       HInstruction* distance,
        uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
-
-  template <typename T, typename U, typename V>
-  T Compute(T x, U y, V max_shift_value) const {
-    static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value,
-                  "V is not the unsigned integer type corresponding to T");
-    return x >> (y & max_shift_value);
+      : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+    DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType()));
+    DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType()));
   }
 
-  HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+  template <typename T>
+  T Compute(T value, int32_t distance, int32_t max_shift_distance) const {
+    return value >> (distance & max_shift_distance);
+  }
+
+  HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE {
     return GetBlock()->GetGraph()->GetIntConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
+        Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc());
   }
-  HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
+  HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE {
     return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+        Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc());
   }
-  HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+  HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED,
+                      HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
+    LOG(FATAL) << DebugName() << " is not defined for the (long, long) case.";
+    UNREACHABLE();
   }
-  HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
-                      HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+  HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED,
+                      HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
     LOG(FATAL) << DebugName() << " is not defined for float values";
     UNREACHABLE();
   }
-  HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
-                      HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+  HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED,
+                      HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
     LOG(FATAL) << DebugName() << " is not defined for double values";
     UNREACHABLE();
   }
@@ -4535,38 +4539,41 @@
 class HUShr : public HBinaryOperation {
  public:
   HUShr(Primitive::Type result_type,
-        HInstruction* left,
-        HInstruction* right,
+        HInstruction* value,
+        HInstruction* distance,
         uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
-
-  template <typename T, typename U, typename V>
-  T Compute(T x, U y, V max_shift_value) const {
-    static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value,
-                  "V is not the unsigned integer type corresponding to T");
-    V ux = static_cast<V>(x);
-    return static_cast<T>(ux >> (y & max_shift_value));
+      : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+    DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType()));
+    DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType()));
   }
 
-  HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+  template <typename T>
+  T Compute(T value, int32_t distance, int32_t max_shift_distance) const {
+    typedef typename std::make_unsigned<T>::type V;
+    V ux = static_cast<V>(value);
+    return static_cast<T>(ux >> (distance & max_shift_distance));
+  }
+
+  HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE {
     return GetBlock()->GetGraph()->GetIntConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
+        Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc());
   }
-  HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
+  HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE {
     return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+        Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc());
   }
-  HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+  HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED,
+                      HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
+    LOG(FATAL) << DebugName() << " is not defined for the (long, long) case.";
+    UNREACHABLE();
   }
-  HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
-                      HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+  HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED,
+                      HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
     LOG(FATAL) << DebugName() << " is not defined for float values";
     UNREACHABLE();
   }
-  HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
-                      HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+  HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED,
+                      HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
     LOG(FATAL) << DebugName() << " is not defined for double values";
     UNREACHABLE();
   }
@@ -4692,45 +4699,43 @@
  public:
   HRor(Primitive::Type result_type, HInstruction* value, HInstruction* distance)
     : HBinaryOperation(result_type, value, distance) {
-    if (kIsDebugBuild) {
-      DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType()));
-      DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType()));
-    }
+    DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType()));
+    DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType()));
   }
 
-  template <typename T, typename U, typename V>
-  T Compute(T x, U y, V max_shift_value) const {
-    static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value,
-                  "V is not the unsigned integer type corresponding to T");
-    V ux = static_cast<V>(x);
-    if ((y & max_shift_value) == 0) {
+  template <typename T>
+  T Compute(T value, int32_t distance, int32_t max_shift_value) const {
+    typedef typename std::make_unsigned<T>::type V;
+    V ux = static_cast<V>(value);
+    if ((distance & max_shift_value) == 0) {
       return static_cast<T>(ux);
     } else {
       const V reg_bits = sizeof(T) * 8;
-      return static_cast<T>(ux >> (y & max_shift_value)) |
-                           (x << (reg_bits - (y & max_shift_value)));
+      return static_cast<T>(ux >> (distance & max_shift_value)) |
+                           (value << (reg_bits - (distance & max_shift_value)));
     }
   }
 
-  HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+  HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE {
     return GetBlock()->GetGraph()->GetIntConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
+        Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc());
   }
-  HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
+  HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE {
     return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+        Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc());
   }
-  HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+  HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED,
+                      HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
+    LOG(FATAL) << DebugName() << " is not defined for the (long, long) case.";
+    UNREACHABLE();
   }
-  HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
-                      HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+  HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED,
+                      HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
     LOG(FATAL) << DebugName() << " is not defined for float values";
     UNREACHABLE();
   }
-  HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
-                      HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+  HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED,
+                      HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
     LOG(FATAL) << DebugName() << " is not defined for double values";
     UNREACHABLE();
   }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index f1c5581..125c00d 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -655,7 +655,7 @@
   // code units is bigger than 128.
   static constexpr size_t kSpaceFilterOptimizingThreshold = 128;
   const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions();
-  if ((compiler_options.GetCompilerFilter() == CompilerOptions::kSpace)
+  if ((compiler_options.GetCompilerFilter() == CompilerFilter::kSpace)
       && (code_item->insns_size_in_code_units_ > kSpaceFilterOptimizingThreshold)) {
     MaybeRecordStat(MethodCompilationStat::kNotCompiledSpaceFilter);
     return nullptr;
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 0ad104e..fc72727 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -47,6 +47,19 @@
   bound_type->GetBlock()->RemoveInstruction(bound_type);
 }
 
+void PrepareForRegisterAllocation::VisitArraySet(HArraySet* instruction) {
+  HInstruction* value = instruction->GetValue();
+  // PrepareForRegisterAllocation::VisitBoundType may have replaced a
+  // BoundType (as value input of this ArraySet) with a NullConstant.
+  // If so, this ArraySet no longer needs a type check.
+  if (value->IsNullConstant()) {
+    DCHECK_EQ(value->GetType(), Primitive::kPrimNot);
+    if (instruction->NeedsTypeCheck()) {
+      instruction->ClearNeedsTypeCheck();
+    }
+  }
+}
+
 void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) {
   // Try to find a static invoke or a new-instance from which this check originated.
   HInstruction* implicit_clinit = nullptr;
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index c90724c..a679148 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -40,6 +40,7 @@
   void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE;
   void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE;
   void VisitBoundType(HBoundType* bound_type) OVERRIDE;
+  void VisitArraySet(HArraySet* instruction) OVERRIDE;
   void VisitClinitCheck(HClinitCheck* check) OVERRIDE;
   void VisitCondition(HCondition* condition) OVERRIDE;
   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 2de0c1b..d5b95d2 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -135,13 +135,13 @@
 TEST_F(PrettyPrinterTest, CFG4) {
   const char* expected =
     "BasicBlock 0, succ: 3\n"
-    "  3: SuspendCheck\n"
-    "  4: Goto 3\n"
+    "  2: SuspendCheck\n"
+    "  3: Goto 3\n"
     "BasicBlock 1, pred: 3, 1, succ: 1\n"
-    "  0: SuspendCheck\n"
-    "  1: Goto 1\n"
+    "  5: SuspendCheck\n"
+    "  0: Goto 1\n"
     "BasicBlock 3, pred: 0, succ: 1\n"
-    "  5: Goto 1\n";
+    "  4: Goto 1\n";
 
   const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
     Instruction::NOP,
@@ -204,20 +204,20 @@
   const char* expected =
     "BasicBlock 0, succ: 1\n"
     "  1: IntConstant [5, 5]\n"
-    "  11: SuspendCheck\n"
-    "  12: Goto 1\n"
+    "  10: SuspendCheck\n"
+    "  11: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 5, 6\n"
     "  5: Equal(1, 1) [6]\n"
     "  6: If(5)\n"
     "BasicBlock 2, pred: 6, 3, succ: 3\n"
     "  7: Goto 3\n"
     "BasicBlock 3, pred: 5, 2, succ: 2\n"
-    "  8: SuspendCheck\n"
-    "  9: Goto 2\n"
+    "  14: SuspendCheck\n"
+    "  8: Goto 2\n"
     "BasicBlock 5, pred: 1, succ: 3\n"
-    "  13: Goto 3\n"
+    "  12: Goto 3\n"
     "BasicBlock 6, pred: 1, succ: 2\n"
-    "  14: Goto 2\n";
+    "  13: Goto 2\n";
 
   const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
     Instruction::CONST_4 | 0 | 0,
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ede0bda..ec6f96f 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -257,11 +257,14 @@
                 "|verify-at-runtime"
                 "|verify-profile"
                 "|interpret-only"
+                "|time"
+                "|space-profile"
                 "|space"
                 "|balanced"
+                "|speed-profile"
                 "|speed"
-                "|everything"
-                "|time):");
+                "|everything-profile"
+                "|everything):");
   UsageError("      select compiler filter.");
   UsageError("      verify-profile requires a --profile(-fd) to also be passed in.");
   UsageError("      Example: --compiler-filter=everything");
@@ -798,10 +801,6 @@
       Usage("Profile file should not be specified with both --profile-file-fd and --profile-file");
     }
 
-    if (compiler_options_->VerifyOnlyProfile() && !have_profile_file && !have_profile_fd) {
-      Usage("verify-profile compiler filter must be used with a profile file or fd");
-    }
-
     if (!parser_options->oat_symbols.empty()) {
       oat_unstripped_ = std::move(parser_options->oat_symbols);
     }
@@ -834,14 +833,14 @@
     // time here, which is orthogonal to space.
     if (compiler_options_->inline_depth_limit_ == CompilerOptions::kUnsetInlineDepthLimit) {
       compiler_options_->inline_depth_limit_ =
-          (compiler_options_->compiler_filter_ == CompilerOptions::kSpace)
+          (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_ == CompilerOptions::kSpace)
+          (compiler_options_->compiler_filter_ == CompilerFilter::kSpace)
           // Implementation of the space filter: limit inlining max code units.
           ? CompilerOptions::kSpaceFilterInlineMaxCodeUnits
           : CompilerOptions::kDefaultInlineMaxCodeUnits;
@@ -1029,11 +1028,8 @@
     key_value_store_->Put(
         OatHeader::kNativeDebuggableKey,
         compiler_options_->GetNativeDebuggable() ? OatHeader::kTrueValue : OatHeader::kFalseValue);
-    if (compiler_options_->IsExtractOnly()) {
-      key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kExtractOnlyValue);
-    } else if (UseProfileGuidedCompilation()) {
-      key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kProfileGuideCompiledValue);
-    }
+    key_value_store_->Put(OatHeader::kCompilerFilter,
+        CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
   }
 
   // Parse the arguments from the command line. In case of an unrecognized option or impossible
@@ -1322,13 +1318,7 @@
         return false;
       }
 
-      if (compiler_options_->IsExtractOnly()) {
-        // ExtractOnly oat files only contain non-quickened DEX code and are
-        // therefore independent of the image file.
-        image_file_location_oat_checksum_ = 0u;
-        image_file_location_oat_data_begin_ = 0u;
-        image_patch_delta_ = 0;
-      } else {
+      if (CompilerFilter::DependsOnImageChecksum(compiler_options_->GetCompilerFilter())) {
         TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
         std::vector<gc::space::ImageSpace*> image_spaces =
             Runtime::Current()->GetHeap()->GetBootImageSpaces();
@@ -1345,6 +1335,10 @@
         if (!image_file_location.empty()) {
           key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location);
         }
+      } else {
+        image_file_location_oat_checksum_ = 0u;
+        image_file_location_oat_data_begin_ = 0u;
+        image_patch_delta_ = 0;
       }
 
       // Open dex files for class path.
@@ -1456,7 +1450,7 @@
         num_methods += dex_file->NumMethodIds();
       }
       if (num_methods <= compiler_options_->GetNumDexMethodsThreshold()) {
-        compiler_options_->SetCompilerFilter(CompilerOptions::kSpeed);
+        compiler_options_->SetCompilerFilter(CompilerFilter::kSpeed);
         VLOG(compiler) << "Below method threshold, compiling anyways";
       }
     }
@@ -1857,7 +1851,7 @@
   }
 
   bool UseProfileGuidedCompilation() const {
-    return !profile_file_.empty() || (profile_file_fd_ != kInvalidFd);
+    return CompilerFilter::DependsOnProfile(compiler_options_->GetCompilerFilter());
   }
 
   bool LoadProfile() {
@@ -1865,7 +1859,7 @@
 
     profile_compilation_info_.reset(new ProfileCompilationInfo());
     ScopedFlock flock;
-    bool success = false;
+    bool success = true;
     std::string error;
     if (profile_file_fd_ != -1) {
       // The file doesn't need to be flushed so don't check the usage.
@@ -1874,7 +1868,7 @@
       if (flock.Init(&file, &error)) {
         success = profile_compilation_info_->Load(profile_file_fd_);
       }
-    } else {
+    } else if (profile_file_ != "") {
       if (flock.Init(profile_file_.c_str(), O_RDONLY, /* block */ true, &error)) {
         success = profile_compilation_info_->Load(flock.GetFile()->Fd());
       }
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index b34bc84..3e420ad 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -84,6 +84,8 @@
   std::string core_oat_location_;
 };
 
+// Disable tests on arm as they are taking too long to run for hammerhead. b/27824283.
+#ifndef __arm__
 TEST_F(OatDumpTest, TestImage) {
   std::string error_msg;
   ASSERT_TRUE(Exec(kModeArt, {}, &error_msg)) << error_msg;
@@ -128,5 +130,5 @@
   std::string error_msg;
   ASSERT_TRUE(Exec(kModeSymbolize, {}, &error_msg)) << error_msg;
 }
-
+#endif
 }  // namespace art
diff --git a/runtime/Android.mk b/runtime/Android.mk
index fc96acf..0c6541e 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -44,6 +44,7 @@
   class_table.cc \
   code_simulator_container.cc \
   common_throws.cc \
+  compiler_filter.cc \
   debugger.cc \
   dex_file.cc \
   dex_file_verifier.cc \
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 729957f..f58af5a 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -72,7 +72,7 @@
   filename_ = getenv("ANDROID_DATA");
   filename_ += "/TmpFile-XXXXXX";
   int fd = mkstemp(&filename_[0]);
-  CHECK_NE(-1, fd);
+  CHECK_NE(-1, fd) << strerror(errno) << " for " << filename_;
   file_.reset(new File(fd, GetFilename(), true));
 }
 
diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc
new file mode 100644
index 0000000..31a1bc1
--- /dev/null
+++ b/runtime/compiler_filter.cc
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiler_filter.h"
+
+#include "utils.h"
+
+namespace art {
+
+bool CompilerFilter::IsCompilationEnabled(Filter filter) {
+  switch (filter) {
+    case CompilerFilter::kVerifyNone:
+    case CompilerFilter::kVerifyAtRuntime:
+    case CompilerFilter::kVerifyProfile:
+    case CompilerFilter::kInterpretOnly: return false;
+
+    case CompilerFilter::kSpaceProfile:
+    case CompilerFilter::kSpace:
+    case CompilerFilter::kBalanced:
+    case CompilerFilter::kTime:
+    case CompilerFilter::kSpeedProfile:
+    case CompilerFilter::kSpeed:
+    case CompilerFilter::kEverythingProfile:
+    case CompilerFilter::kEverything: return true;
+  }
+  UNREACHABLE();
+}
+
+bool CompilerFilter::IsVerificationEnabled(Filter filter) {
+  switch (filter) {
+    case CompilerFilter::kVerifyNone:
+    case CompilerFilter::kVerifyAtRuntime: return false;
+
+    case CompilerFilter::kVerifyProfile:
+    case CompilerFilter::kInterpretOnly:
+    case CompilerFilter::kSpaceProfile:
+    case CompilerFilter::kSpace:
+    case CompilerFilter::kBalanced:
+    case CompilerFilter::kTime:
+    case CompilerFilter::kSpeedProfile:
+    case CompilerFilter::kSpeed:
+    case CompilerFilter::kEverythingProfile:
+    case CompilerFilter::kEverything: return true;
+  }
+  UNREACHABLE();
+}
+
+bool CompilerFilter::DependsOnImageChecksum(Filter filter) {
+  // We run dex2dex with verification, so the oat file will depend on the
+  // image checksum if verification is enabled.
+  return IsVerificationEnabled(filter);
+}
+
+bool CompilerFilter::DependsOnProfile(Filter filter) {
+  switch (filter) {
+    case CompilerFilter::kVerifyNone:
+    case CompilerFilter::kVerifyAtRuntime:
+    case CompilerFilter::kInterpretOnly:
+    case CompilerFilter::kSpace:
+    case CompilerFilter::kBalanced:
+    case CompilerFilter::kTime:
+    case CompilerFilter::kSpeed:
+    case CompilerFilter::kEverything: return false;
+
+    case CompilerFilter::kVerifyProfile:
+    case CompilerFilter::kSpaceProfile:
+    case CompilerFilter::kSpeedProfile:
+    case CompilerFilter::kEverythingProfile: return true;
+  }
+  UNREACHABLE();
+}
+
+bool CompilerFilter::IsAsGoodAs(Filter current, Filter target) {
+  return current >= target;
+}
+
+std::string CompilerFilter::NameOfFilter(Filter filter) {
+  switch (filter) {
+    case CompilerFilter::kVerifyNone: return "verify-none";
+    case CompilerFilter::kVerifyAtRuntime: return "verify-at-runtime";
+    case CompilerFilter::kVerifyProfile: return "verify-profile";
+    case CompilerFilter::kInterpretOnly: return "interpret-only";
+    case CompilerFilter::kSpaceProfile: return "space-profile";
+    case CompilerFilter::kSpace: return "space";
+    case CompilerFilter::kBalanced: return "balanced";
+    case CompilerFilter::kTime: return "time";
+    case CompilerFilter::kSpeedProfile: return "speed-profile";
+    case CompilerFilter::kSpeed: return "speed";
+    case CompilerFilter::kEverythingProfile: return "everything-profile";
+    case CompilerFilter::kEverything: return "everything";
+  }
+  UNREACHABLE();
+}
+
+bool CompilerFilter::ParseCompilerFilter(const char* option, Filter* filter) {
+  CHECK(filter != nullptr);
+
+  if (strcmp(option, "verify-none") == 0) {
+    *filter = kVerifyNone;
+  } else if (strcmp(option, "interpret-only") == 0) {
+    *filter = kInterpretOnly;
+  } else if (strcmp(option, "verify-profile") == 0) {
+    *filter = kVerifyProfile;
+  } else if (strcmp(option, "verify-at-runtime") == 0) {
+    *filter = kVerifyAtRuntime;
+  } else if (strcmp(option, "space") == 0) {
+    *filter = kSpace;
+  } else if (strcmp(option, "space-profile") == 0) {
+    *filter = kSpaceProfile;
+  } else if (strcmp(option, "balanced") == 0) {
+    *filter = kBalanced;
+  } else if (strcmp(option, "speed") == 0) {
+    *filter = kSpeed;
+  } else if (strcmp(option, "speed-profile") == 0) {
+    *filter = kSpeedProfile;
+  } else if (strcmp(option, "everything") == 0) {
+    *filter = kEverything;
+  } else if (strcmp(option, "everything-profile") == 0) {
+    *filter = kEverythingProfile;
+  } else if (strcmp(option, "time") == 0) {
+    *filter = kTime;
+  } else {
+    return false;
+  }
+  return true;
+}
+
+std::ostream& operator<<(std::ostream& os, const CompilerFilter::Filter& rhs) {
+  return os << CompilerFilter::NameOfFilter(rhs);
+}
+
+}  // namespace art
diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h
new file mode 100644
index 0000000..1bea8b4
--- /dev/null
+++ b/runtime/compiler_filter.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_COMPILER_FILTER_H_
+#define ART_RUNTIME_COMPILER_FILTER_H_
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+namespace art {
+
+class CompilerFilter FINAL {
+ public:
+  // Note: Order here matters. Later filter choices are considered "as good
+  // as" earlier filter choices.
+  enum Filter {
+    kVerifyNone,          // Skip verification and compile nothing except JNI stubs.
+    kVerifyAtRuntime,     // Only compile JNI stubs and verify at runtime.
+    kVerifyProfile,       // Verify only the classes in the profile.
+    kInterpretOnly,       // Verify, and compile only JNI stubs.
+    kTime,                // Compile methods, but minimize compilation time.
+    kSpaceProfile,        // Maximize space savings based on profile.
+    kSpace,               // Maximize space savings.
+    kBalanced,            // Good performance return on compilation investment.
+    kSpeedProfile,        // Maximize runtime performance based on profile.
+    kSpeed,               // Maximize runtime performance.
+    kEverythingProfile,   // Compile everything capable of being compiled based on profile.
+    kEverything,          // Compile everything capable of being compiled.
+  };
+
+  // Returns true if an oat file with this compiler filter contains
+  // compiled executable code.
+  static bool IsCompilationEnabled(Filter filter);
+
+  // Returns true if this compiler filter requires running verification.
+  static bool IsVerificationEnabled(Filter filter);
+
+  // Returns true if an oat file with this compiler filter depends on the
+  // boot image checksum.
+  static bool DependsOnImageChecksum(Filter filter);
+
+  // Returns true if an oat file with this compiler filter depends on a
+  // profile.
+  static bool DependsOnProfile(Filter filter);
+
+  // Returns true if the 'current' compiler filter is considered at least as
+  // good as the 'target' compilation type.
+  // For example: kSpeed is as good as kInterpretOnly, but kInterpretOnly is
+  // not as good as kSpeed.
+  static bool IsAsGoodAs(Filter current, Filter target);
+
+  // Return the flag name of the given filter.
+  // For example: given kVerifyAtRuntime, returns "verify-at-runtime".
+  // The name returned corresponds to the name accepted by
+  // ParseCompilerFilter.
+  static std::string NameOfFilter(Filter filter);
+
+  // Parse the compiler filter from the given name.
+  // Returns true and sets filter to the parsed value if name refers to a
+  // valid filter. Returns false if no filter matches that name.
+  // 'filter' must be non-null.
+  static bool ParseCompilerFilter(const char* name, /*out*/Filter* filter);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CompilerFilter);
+};
+
+std::ostream& operator<<(std::ostream& os, const CompilerFilter::Filter& rhs);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_COMPILER_FILTER_H_
diff --git a/runtime/compiler_filter_test.cc b/runtime/compiler_filter_test.cc
new file mode 100644
index 0000000..c603be6
--- /dev/null
+++ b/runtime/compiler_filter_test.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiler_filter.h"
+
+#include <gtest/gtest.h>
+
+namespace art {
+
+static void TestCompilerFilterName(CompilerFilter::Filter filter, std::string name) {
+  CompilerFilter::Filter parsed;
+  EXPECT_TRUE(CompilerFilter::ParseCompilerFilter(name.c_str(), &parsed));
+  EXPECT_EQ(filter, parsed);
+
+  EXPECT_EQ(name, CompilerFilter::NameOfFilter(filter));
+}
+
+// Verify the dexopt status values from dalvik.system.DexFile
+// match the OatFileAssistant::DexOptStatus values.
+TEST(CompilerFilterTest, ParseCompilerFilter) {
+  CompilerFilter::Filter filter;
+
+  TestCompilerFilterName(CompilerFilter::kVerifyNone, "verify-none");
+  TestCompilerFilterName(CompilerFilter::kVerifyAtRuntime, "verify-at-runtime");
+  TestCompilerFilterName(CompilerFilter::kVerifyProfile, "verify-profile");
+  TestCompilerFilterName(CompilerFilter::kInterpretOnly, "interpret-only");
+  TestCompilerFilterName(CompilerFilter::kTime, "time");
+  TestCompilerFilterName(CompilerFilter::kSpaceProfile, "space-profile");
+  TestCompilerFilterName(CompilerFilter::kSpace, "space");
+  TestCompilerFilterName(CompilerFilter::kBalanced, "balanced");
+  TestCompilerFilterName(CompilerFilter::kSpeedProfile, "speed-profile");
+  TestCompilerFilterName(CompilerFilter::kSpeed, "speed");
+  TestCompilerFilterName(CompilerFilter::kEverythingProfile, "everything-profile");
+  TestCompilerFilterName(CompilerFilter::kEverything, "everything");
+
+  EXPECT_FALSE(CompilerFilter::ParseCompilerFilter("super-awesome-filter", &filter));
+}
+
+}  // namespace art
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 2c487fe..bd84d0d 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -1653,14 +1653,14 @@
     if (i < kNumThreadLocalSizeBrackets) {
       numOfPages[i] = 1;
     } else if (i < (kNumThreadLocalSizeBrackets + kNumRegularSizeBrackets) / 2) {
-      numOfPages[i] = 4;
+      numOfPages[i] = 1;
     } else if (i < kNumRegularSizeBrackets) {
-      numOfPages[i] = 8;
+      numOfPages[i] = 1;
     } else if (i == kNumOfSizeBrackets - 2) {
-      numOfPages[i] = 16;
+      numOfPages[i] = 2;
     } else {
       DCHECK_EQ(i, kNumOfSizeBrackets - 1);
-      numOfPages[i] = 32;
+      numOfPages[i] = 4;
     }
     if (kTraceRosAlloc) {
       LOG(INFO) << "numOfPages[" << i << "]=" << numOfPages[i];
@@ -2102,6 +2102,94 @@
   }
 }
 
+void RosAlloc::DumpStats(std::ostream& os) {
+  Thread* self = Thread::Current();
+  CHECK(Locks::mutator_lock_->IsExclusiveHeld(self))
+      << "The mutator locks isn't exclusively locked at " << __PRETTY_FUNCTION__;
+  size_t num_large_objects = 0;
+  size_t num_pages_large_objects = 0;
+  // These arrays are zero initialized.
+  std::unique_ptr<size_t[]> num_runs(new size_t[kNumOfSizeBrackets]());
+  std::unique_ptr<size_t[]> num_pages_runs(new size_t[kNumOfSizeBrackets]());
+  std::unique_ptr<size_t[]> num_slots(new size_t[kNumOfSizeBrackets]());
+  std::unique_ptr<size_t[]> num_used_slots(new size_t[kNumOfSizeBrackets]());
+  std::unique_ptr<size_t[]> num_metadata_bytes(new size_t[kNumOfSizeBrackets]());
+  ReaderMutexLock rmu(self, bulk_free_lock_);
+  MutexLock lock_mu(self, lock_);
+  for (size_t i = 0; i < page_map_size_; ) {
+    uint8_t pm = page_map_[i];
+    switch (pm) {
+      case kPageMapReleased:
+      case kPageMapEmpty:
+        ++i;
+        break;
+      case kPageMapLargeObject: {
+        size_t num_pages = 1;
+        size_t idx = i + 1;
+        while (idx < page_map_size_ && page_map_[idx] == kPageMapLargeObjectPart) {
+          num_pages++;
+          idx++;
+        }
+        num_large_objects++;
+        num_pages_large_objects += num_pages;
+        i += num_pages;
+        break;
+      }
+      case kPageMapLargeObjectPart:
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm) << std::endl
+                   << DumpPageMap();
+        break;
+      case kPageMapRun: {
+        Run* run = reinterpret_cast<Run*>(base_ + i * kPageSize);
+        size_t idx = run->size_bracket_idx_;
+        size_t num_pages = numOfPages[idx];
+        num_runs[idx]++;
+        num_pages_runs[idx] += num_pages;
+        num_slots[idx] += numOfSlots[idx];
+        size_t num_free_slots = run->NumberOfFreeSlots();
+        num_used_slots[idx] += numOfSlots[idx] - num_free_slots;
+        num_metadata_bytes[idx] += headerSizes[idx];
+        i += num_pages;
+        break;
+      }
+      case kPageMapRunPart:
+        // Fall-through.
+      default:
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm) << std::endl
+                   << DumpPageMap();
+        break;
+    }
+  }
+  os << "RosAlloc stats:\n";
+  for (size_t i = 0; i < kNumOfSizeBrackets; ++i) {
+    os << "Bracket " << i << " (" << bracketSizes[i] << "):"
+       << " #runs=" << num_runs[i]
+       << " #pages=" << num_pages_runs[i]
+       << " (" << PrettySize(num_pages_runs[i] * kPageSize) << ")"
+       << " #metadata_bytes=" << PrettySize(num_metadata_bytes[i])
+       << " #slots=" << num_slots[i] << " (" << PrettySize(num_slots[i] * bracketSizes[i]) << ")"
+       << " #used_slots=" << num_used_slots[i]
+       << " (" << PrettySize(num_used_slots[i] * bracketSizes[i]) << ")\n";
+  }
+  os << "Large #allocations=" << num_large_objects
+     << " #pages=" << num_pages_large_objects
+     << " (" << PrettySize(num_pages_large_objects * kPageSize) << ")\n";
+  size_t total_num_pages = 0;
+  size_t total_metadata_bytes = 0;
+  size_t total_allocated_bytes = 0;
+  for (size_t i = 0; i < kNumOfSizeBrackets; ++i) {
+    total_num_pages += num_pages_runs[i];
+    total_metadata_bytes += num_metadata_bytes[i];
+    total_allocated_bytes += num_used_slots[i] * bracketSizes[i];
+  }
+  total_num_pages += num_pages_large_objects;
+  total_allocated_bytes += num_pages_large_objects * kPageSize;
+  os << "Total #total_bytes=" << PrettySize(total_num_pages * kPageSize)
+     << " #metadata_bytes=" << PrettySize(total_metadata_bytes)
+     << " #used_bytes=" << PrettySize(total_allocated_bytes) << "\n";
+  os << "\n";
+}
+
 }  // namespace allocator
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index b12cb5b..1fa2d1a 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -928,6 +928,9 @@
   void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes)
       REQUIRES(!bulk_free_lock_, !lock_);
 
+  void DumpStats(std::ostream& os)
+      REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_) REQUIRES(!bulk_free_lock_);
+
  private:
   friend std::ostream& operator<<(std::ostream& os, const RosAlloc::PageMapKind& rhs);
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index a96847f..01db90a 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -116,6 +116,8 @@
 
 // For deterministic compilation, we need the heap to be at a well-known address.
 static constexpr uint32_t kAllocSpaceBeginForDeterministicAoT = 0x40000000;
+// Dump the rosalloc stats on SIGQUIT.
+static constexpr bool kDumpRosAllocStatsOnSigQuit = false;
 
 static inline bool CareAboutPauseTimes() {
   return Runtime::Current()->InJankPerceptibleProcessState();
@@ -1179,6 +1181,10 @@
     }
   }
 
+  if (kDumpRosAllocStatsOnSigQuit && rosalloc_space_ != nullptr) {
+    rosalloc_space_->DumpStats(os);
+  }
+
   BaseMutex::DumpAll(os);
 }
 
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 203d3bc..b016095 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -368,6 +368,11 @@
   SetFootprintLimit(footprint_limit);
 }
 
+void RosAllocSpace::DumpStats(std::ostream& os) {
+  ScopedSuspendAll ssa(__FUNCTION__);
+  rosalloc_->DumpStats(os);
+}
+
 }  // namespace space
 
 namespace allocator {
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index bc14738..b175fbf 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -144,6 +144,8 @@
     rosalloc_->LogFragmentationAllocFailure(os, failed_alloc_bytes);
   }
 
+  void DumpStats(std::ostream& os);
+
  protected:
   RosAllocSpace(MemMap* mem_map, size_t initial_size, const std::string& name,
                 allocator::RosAlloc* rosalloc, uint8_t* begin, uint8_t* end, uint8_t* limit,
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index f1e0fa7..3397989 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -348,7 +348,8 @@
 static jint GetDexOptNeeded(JNIEnv* env,
                             const char* filename,
                             const char* instruction_set,
-                            const int target_compilation_type_mask) {
+                            const char* compiler_filter_name,
+                            bool profile_changed) {
   if ((filename == nullptr) || !OS::FileExists(filename)) {
     LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist";
     ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
@@ -365,17 +366,24 @@
     return -1;
   }
 
+  CompilerFilter::Filter filter;
+  if (!CompilerFilter::ParseCompilerFilter(compiler_filter_name, &filter)) {
+    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
+    std::string message(StringPrintf("Compiler filter %s is invalid.", compiler_filter_name));
+    env->ThrowNew(iae.get(), message.c_str());
+    return -1;
+  }
+
   // TODO: Verify the dex location is well formed, and throw an IOException if
   // not?
-  OatFileAssistant oat_file_assistant(filename, target_compilation_type_mask,
-      target_instruction_set, false);
+
+  OatFileAssistant oat_file_assistant(filename, target_instruction_set, profile_changed, false);
 
   // Always treat elements of the bootclasspath as up-to-date.
   if (oat_file_assistant.IsInBootClassPath()) {
     return OatFileAssistant::kNoDexOptNeeded;
   }
-
-  return oat_file_assistant.GetDexOptNeeded();
+  return oat_file_assistant.GetDexOptNeeded(filter);
 }
 
 static jint DexFile_getDexOptNeeded(JNIEnv* env,
@@ -393,10 +401,18 @@
     return -1;
   }
 
+  // TODO: Take profile changed and compiler filter as arguments.
+  // For now, we use "speed" by default, unless EXTRACT_ONLY = 0x4 was
+  // included in the mask.
+  const char* compiler_filter = "speed";
+  if (javaTargetCompilationTypeMask & 0x4) {
+    compiler_filter = "verify-at-runtime";
+  }
   return GetDexOptNeeded(env,
                          filename.c_str(),
                          instruction_set.c_str(),
-                         javaTargetCompilationTypeMask);
+                         compiler_filter,
+                         /*profile_changed*/false);
 }
 
 // public API
@@ -407,7 +423,8 @@
       env,
       filename.c_str(),
       instruction_set,
-      OatFileAssistant::kFullCompilation | OatFileAssistant::kProfileGuideCompilation);
+      "speed-profile",
+      /*profile_changed*/false);
   return (status != OatFileAssistant::kNoDexOptNeeded) ? JNI_TRUE : JNI_FALSE;
 }
 
diff --git a/runtime/oat.cc b/runtime/oat.cc
index ed99cba..d13999a 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -29,8 +29,6 @@
 constexpr uint8_t OatHeader::kOatVersion[4];
 constexpr const char OatHeader::kTrueValue[];
 constexpr const char OatHeader::kFalseValue[];
-constexpr const char OatHeader::kExtractOnlyValue[];
-constexpr const char OatHeader::kProfileGuideCompiledValue[];
 
 static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
   size_t estimate = 0U;
@@ -472,16 +470,13 @@
   return IsKeyEnabled(OatHeader::kNativeDebuggableKey);
 }
 
-bool OatHeader::IsExtractOnly() const {
-  return KeyHasValue(kCompilationType,
-                     kExtractOnlyValue,
-                     sizeof(kExtractOnlyValue));
-}
-
-bool OatHeader::IsProfileGuideCompiled() const {
-  return KeyHasValue(kCompilationType,
-                     kProfileGuideCompiledValue,
-                     sizeof(kProfileGuideCompiledValue));
+CompilerFilter::Filter OatHeader::GetCompilerFilter() const {
+  CompilerFilter::Filter filter;
+  const char* key_value = GetStoreValueByKey(kCompilerFilter);
+  CHECK(key_value != nullptr) << "compiler-filter not found in oat header";
+  CHECK(CompilerFilter::ParseCompilerFilter(key_value, &filter))
+      << "Invalid compiler-filter in oat header: " << key_value;
+  return filter;
 }
 
 bool OatHeader::KeyHasValue(const char* key, const char* value, size_t value_size) const {
diff --git a/runtime/oat.h b/runtime/oat.h
index 1d6c076..0dcc52e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -21,6 +21,7 @@
 
 #include "arch/instruction_set.h"
 #include "base/macros.h"
+#include "compiler_filter.h"
 #include "dex_file.h"
 #include "safe_map.h"
 
@@ -31,7 +32,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '7', '5', '\0' };
+  static constexpr uint8_t kOatVersion[] = { '0', '7', '6', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
@@ -39,14 +40,12 @@
   static constexpr const char* kPicKey = "pic";
   static constexpr const char* kDebuggableKey = "debuggable";
   static constexpr const char* kNativeDebuggableKey = "native-debuggable";
-  static constexpr const char* kCompilationType = "compilation-type";
+  static constexpr const char* kCompilerFilter = "compiler-filter";
   static constexpr const char* kClassPathKey = "classpath";
   static constexpr const char* kBootClassPath = "bootclasspath";
 
   static constexpr const char kTrueValue[] = "true";
   static constexpr const char kFalseValue[] = "false";
-  static constexpr const char kExtractOnlyValue[] = "extract-only";
-  static constexpr const char kProfileGuideCompiledValue[] = "profile-guide";
 
 
   static OatHeader* Create(InstructionSet instruction_set,
@@ -112,8 +111,7 @@
   bool IsPic() const;
   bool IsDebuggable() const;
   bool IsNativeDebuggable() const;
-  bool IsExtractOnly() const;
-  bool IsProfileGuideCompiled() const;
+  CompilerFilter::Filter GetCompilerFilter() const;
 
  private:
   bool KeyHasValue(const char* key, const char* value, size_t value_size) const;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 033ea56..9ae033f 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1257,12 +1257,8 @@
   return GetOatHeader().IsDebuggable();
 }
 
-bool OatFile::IsExtractOnly() const {
-  return GetOatHeader().IsExtractOnly();
-}
-
-bool OatFile::IsProfileGuideCompiled() const {
-  return GetOatHeader().IsProfileGuideCompiled();
+CompilerFilter::Filter OatFile::GetCompilerFilter() const {
+  return GetOatHeader().GetCompilerFilter();
 }
 
 static constexpr char kDexClassPathEncodingSeparator = '*';
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 7af77ae..21aeab4 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -92,9 +92,7 @@
   // Indicates whether the oat file was compiled with full debugging capability.
   bool IsDebuggable() const;
 
-  bool IsExtractOnly() const;
-
-  bool IsProfileGuideCompiled() const;
+  CompilerFilter::Filter GetCompilerFilter() const;
 
   const std::string& GetLocation() const {
     return location_;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index cbc0ec6..096296b 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -44,19 +44,18 @@
 namespace art {
 
 OatFileAssistant::OatFileAssistant(const char* dex_location,
-                                   const int target_compilation_type_mask,
                                    const InstructionSet isa,
+                                   bool profile_changed,
                                    bool load_executable)
-    : OatFileAssistant(dex_location, nullptr, target_compilation_type_mask, isa, load_executable)
+    : OatFileAssistant(dex_location, nullptr, isa, profile_changed, load_executable)
 { }
 
 OatFileAssistant::OatFileAssistant(const char* dex_location,
                                    const char* oat_location,
-                                   const int target_compilation_type_mask,
                                    const InstructionSet isa,
+                                   bool profile_changed,
                                    bool load_executable)
-    : target_compilation_type_mask_(target_compilation_type_mask), isa_(isa),
-      load_executable_(load_executable) {
+    : isa_(isa), profile_changed_(profile_changed), load_executable_(load_executable) {
   CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
   dex_location_.assign(dex_location);
 
@@ -116,42 +115,78 @@
   return true;
 }
 
-// Returns the compilation mode of the given oat file.
-static OatFileAssistant::CompilationType GetCompilationType(const OatFile& oat_file) {
-    if (oat_file.IsExtractOnly()) {
-      return OatFileAssistant::kExtractOnly;
-    }
-    if (oat_file.IsProfileGuideCompiled()) {
-      return OatFileAssistant::kProfileGuideCompilation;
-    }
-    // Assume that if the oat files is not extract-only or profile-guide compiled
-    // then it must be fully compiled.
-    // NB: this does not necessary mean that the oat file is actually fully compiled. It
-    // might have been compiled in a different way (e.g. interpret-only) which does
-    // not record a type in the header.
-    return OatFileAssistant::kFullCompilation;
+bool OatFileAssistant::OatFileCompilerFilterIsOkay(CompilerFilter::Filter target) {
+  const OatFile* oat_file = GetOatFile();
+  if (oat_file != nullptr) {
+    CompilerFilter::Filter current = oat_file->GetCompilerFilter();
+    return CompilerFilter::IsAsGoodAs(current, target);
+  }
+  return false;
 }
 
-OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() {
-  if (OatFileIsUpToDate() || OdexFileIsUpToDate()) {
-    return kNoDexOptNeeded;
+bool OatFileAssistant::OdexFileCompilerFilterIsOkay(CompilerFilter::Filter target) {
+  const OatFile* odex_file = GetOdexFile();
+  if (odex_file != nullptr) {
+    CompilerFilter::Filter current = odex_file->GetCompilerFilter();
+    return CompilerFilter::IsAsGoodAs(current, target);
+  }
+  return false;
+}
+
+OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target) {
+  bool compilation_desired = CompilerFilter::IsCompilationEnabled(target);
+
+  // See if the oat file is in good shape as is.
+  bool oat_okay = OatFileCompilerFilterIsOkay(target);
+  if (oat_okay) {
+    if (compilation_desired) {
+      if (OatFileIsUpToDate()) {
+        return kNoDexOptNeeded;
+      }
+    } else {
+      if (!OatFileIsOutOfDate()) {
+        return kNoDexOptNeeded;
+      }
+    }
   }
 
-  if (OdexFileNeedsRelocation()) {
-    return kPatchOatNeeded;
+  // See if the odex file is in good shape as is.
+  bool odex_okay = OdexFileCompilerFilterIsOkay(target);
+  if (odex_okay) {
+    if (compilation_desired) {
+      if (OdexFileIsUpToDate()) {
+        return kNoDexOptNeeded;
+      }
+    } else {
+      if (!OdexFileIsOutOfDate()) {
+        return kNoDexOptNeeded;
+      }
+    }
   }
 
-  if (OatFileNeedsRelocation()) {
-    return kSelfPatchOatNeeded;
+  // See if we can get an up-to-date file by running patchoat.
+  if (compilation_desired) {
+    if (odex_okay && OdexFileNeedsRelocation()) {
+      // TODO: don't return kPatchOatNeeded if the odex file contains no
+      // patch information.
+      return kPatchOatNeeded;
+    }
+
+    if (oat_okay && OatFileNeedsRelocation()) {
+      // TODO: don't return kSelfPatchOatNeeded if the oat file contains no
+      // patch information.
+      return kSelfPatchOatNeeded;
+    }
   }
 
+  // We can only run dex2oat if there are original dex files.
   return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
 }
 
-bool OatFileAssistant::MakeUpToDate(std::string* error_msg) {
-  switch (GetDexOptNeeded()) {
+bool OatFileAssistant::MakeUpToDate(CompilerFilter::Filter target, std::string* error_msg) {
+  switch (GetDexOptNeeded(target)) {
     case kNoDexOptNeeded: return true;
-    case kDex2OatNeeded: return GenerateOatFile(error_msg);
+    case kDex2OatNeeded: return GenerateOatFile(target, error_msg);
     case kPatchOatNeeded: return RelocateOatFile(OdexFileName(), error_msg);
     case kSelfPatchOatNeeded: return RelocateOatFile(OatFileName(), error_msg);
   }
@@ -410,11 +445,6 @@
 }
 
 bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) {
-  // Verify the file satisfies the desired compilation type.
-  if ((target_compilation_type_mask_ & GetCompilationType(file)) == 0) {
-    return true;
-  }
-
   // Verify the dex checksum.
   // Note: GetOatDexFile will return null if the dex checksum doesn't match
   // what we provide, which verifies the primary dex checksum for us.
@@ -457,36 +487,38 @@
     }
   }
 
-  if (file.IsExtractOnly()) {
-    VLOG(oat) << "Oat file is extract-only. Image checksum test skipped.";
-    if (kIsDebugBuild) {
-      // Sanity check that no classes have compiled code. Does not test that
-      // the DEX code has not been quickened.
-      std::string error_msg;
-      for (const OatFile::OatDexFile* current : file.GetOatDexFiles()) {
-        std::unique_ptr<const DexFile> dex_file = current->OpenDexFile(&error_msg);
-        DCHECK(dex_file != nullptr);
-        for (size_t i = 0, e = dex_file->NumClassDefs(); i < e; ++i) {
-          DCHECK_EQ(current->GetOatClass(i).GetType(), kOatClassNoneCompiled);
-        }
-      }
-    }
-    return false;
-  }
+  CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter();
+  VLOG(oat) << "Compiler filter for " << file.GetLocation() << " is " << current_compiler_filter;
 
   // Verify the image checksum
-  const ImageInfo* image_info = GetImageInfo();
-  if (image_info == nullptr) {
-    VLOG(oat) << "No image for oat image checksum to match against.";
-    return true;
+  if (CompilerFilter::DependsOnImageChecksum(current_compiler_filter)) {
+    const ImageInfo* image_info = GetImageInfo();
+    if (image_info == nullptr) {
+      VLOG(oat) << "No image for oat image checksum to match against.";
+      return true;
+    }
+
+    if (file.GetOatHeader().GetImageFileLocationOatChecksum() != image_info->oat_checksum) {
+      VLOG(oat) << "Oat image checksum does not match image checksum.";
+      return true;
+    }
+  } else {
+    VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
   }
 
-  if (file.GetOatHeader().GetImageFileLocationOatChecksum() != image_info->oat_checksum) {
-    VLOG(oat) << "Oat image checksum does not match image checksum.";
-    return true;
+  // Verify the profile hasn't changed recently.
+  // TODO: Move this check to OatFileCompilerFilterIsOkay? Nothing bad should
+  // happen if we use an oat file compiled with an out-of-date profile.
+  if (CompilerFilter::DependsOnProfile(current_compiler_filter)) {
+    if (profile_changed_) {
+      VLOG(oat) << "The profile has changed recently.";
+      return true;
+    }
+  } else {
+    VLOG(oat) << "Profile check skipped for compiler filter " << current_compiler_filter;
   }
 
-  // The checksums are all good; the dex file is not out of date.
+  // Everything looks good; the dex file is not out of date.
   return false;
 }
 
@@ -499,40 +531,44 @@
     return false;
   }
 
-  if (file.IsPic() || file.IsExtractOnly()) {
-    // Oat files compiled in PIC mode do not require relocation and extract-only
-    // oat files do not contain any compiled code. Skip the relocation test.
-    VLOG(oat) << "Oat relocation test skipped.";
-    return true;
-  }
+  CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter();
 
-  const ImageInfo* image_info = GetImageInfo();
-  if (image_info == nullptr) {
-    VLOG(oat) << "No image to check oat relocation against.";
-    return false;
-  }
+  if (CompilerFilter::IsCompilationEnabled(current_compiler_filter)) {
+    if (!file.IsPic()) {
+      const ImageInfo* image_info = GetImageInfo();
+      if (image_info == nullptr) {
+        VLOG(oat) << "No image to check oat relocation against.";
+        return false;
+      }
 
-  // Verify the oat_data_begin recorded for the image in the oat file matches
-  // the actual oat_data_begin for boot.oat in the image.
-  const OatHeader& oat_header = file.GetOatHeader();
-  uintptr_t oat_data_begin = oat_header.GetImageFileLocationOatDataBegin();
-  if (oat_data_begin != image_info->oat_data_begin) {
-    VLOG(oat) << file.GetLocation() <<
-      ": Oat file image oat_data_begin (" << oat_data_begin << ")"
-      << " does not match actual image oat_data_begin ("
-      << image_info->oat_data_begin << ")";
-    return false;
-  }
+      // Verify the oat_data_begin recorded for the image in the oat file matches
+      // the actual oat_data_begin for boot.oat in the image.
+      const OatHeader& oat_header = file.GetOatHeader();
+      uintptr_t oat_data_begin = oat_header.GetImageFileLocationOatDataBegin();
+      if (oat_data_begin != image_info->oat_data_begin) {
+        VLOG(oat) << file.GetLocation() <<
+          ": Oat file image oat_data_begin (" << oat_data_begin << ")"
+          << " does not match actual image oat_data_begin ("
+          << image_info->oat_data_begin << ")";
+        return false;
+      }
 
-  // Verify the oat_patch_delta recorded for the image in the oat file matches
-  // the actual oat_patch_delta for the image.
-  int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
-  if (oat_patch_delta != image_info->patch_delta) {
-    VLOG(oat) << file.GetLocation() <<
-      ": Oat file image patch delta (" << oat_patch_delta << ")"
-      << " does not match actual image patch delta ("
-      << image_info->patch_delta << ")";
-    return false;
+      // Verify the oat_patch_delta recorded for the image in the oat file matches
+      // the actual oat_patch_delta for the image.
+      int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
+      if (oat_patch_delta != image_info->patch_delta) {
+        VLOG(oat) << file.GetLocation() <<
+          ": Oat file image patch delta (" << oat_patch_delta << ")"
+          << " does not match actual image patch delta ("
+          << image_info->patch_delta << ")";
+        return false;
+      }
+    } else {
+      // Oat files compiled in PIC mode do not require relocation.
+      VLOG(oat) << "Oat relocation test skipped for PIC oat file";
+    }
+  } else {
+    VLOG(oat) << "Oat relocation test skipped for compiler filter " << current_compiler_filter;
   }
   return true;
 }
@@ -589,18 +625,9 @@
   return true;
 }
 
-bool OatFileAssistant::GenerateOatFile(std::string* error_msg) {
+bool OatFileAssistant::GenerateOatFile(CompilerFilter::Filter target, std::string* error_msg) {
   CHECK(error_msg != nullptr);
 
-  // TODO: Currently we only know how to make a fully-compiled oat file.
-  // Perhaps we should support generating other kinds of oat files?
-  if ((target_compilation_type_mask_ & kFullCompilation) == 0) {
-    *error_msg = "Generation of oat file for dex location " + dex_location_
-      + " not attempted because full compilation was not specified"
-      + " as an acceptable target compilation type.";
-    return false;
-  }
-
   Runtime* runtime = Runtime::Current();
   if (!runtime->IsDex2OatEnabled()) {
     *error_msg = "Generation of oat file for dex location " + dex_location_
@@ -642,6 +669,7 @@
   args.push_back("--dex-file=" + dex_location_);
   args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
   args.push_back("--oat-location=" + oat_file_name);
+  args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(target));
 
   if (!Dex2Oat(args, error_msg)) {
     // Manually delete the file. This ensures there is no garbage left over if
@@ -751,8 +779,7 @@
 
 std::string OatFileAssistant::DalvikCacheDirectory() {
   // Note: We don't cache this, because it will only be called once by
-  // OatFileName, and we don't care about the performance of the profiling
-  // code, which isn't used in practice.
+  // OatFileName.
 
   // TODO: The work done in GetDalvikCache is overkill for what we need.
   // Ideally a new API for getting the DalvikCacheDirectory the way we want
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 893aea2..452cd84 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -24,6 +24,7 @@
 #include "arch/instruction_set.h"
 #include "base/scoped_flock.h"
 #include "base/unix_file/fd_file.h"
+#include "compiler_filter.h"
 #include "oat_file.h"
 #include "os.h"
 #include "profiler.h"
@@ -85,20 +86,6 @@
     kOatUpToDate,
   };
 
-  // Represents the different compilation types of oat files that OatFileAssitant
-  // and external GetDexOptNeeded callers care about.
-  // Note: these should be able to be used as part of a mask.
-  enum CompilationType {
-    // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_FULL = 1
-    kFullCompilation = 1,
-
-    // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_PROFILE_GUIDE = 2
-    kProfileGuideCompilation = 2,
-
-    // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_EXTRACT_ONLY = 4
-    kExtractOnly = 4,
-  };
-
   // Constructs an OatFileAssistant object to assist the oat file
   // corresponding to the given dex location with the target instruction set.
   //
@@ -110,27 +97,26 @@
   // Note: Currently the dex_location must have an extension.
   // TODO: Relax this restriction?
   //
-  // The target compilation type specifies a set of CompilationTypes that
-  // should be considered up to date. An oat file compiled in a way not
-  // included in the set is considered out of date. For example, to consider
-  // otherwise up-to-date fully compiled and profile-guide compiled oat
-  // files as up to date, but to consider extract-only files as out of date,
-  // specify: (kFullCompilation | kProfileGuideCompilation).
-  //
   // The isa should be either the 32 bit or 64 bit variant for the current
   // device. For example, on an arm device, use arm or arm64. An oat file can
   // be loaded executable only if the ISA matches the current runtime.
+  //
+  // profile_changed should be true if the profile has recently changed
+  // for this dex location.
+  //
+  // load_executable should be true if the caller intends to try and load
+  // executable code for this dex location.
   OatFileAssistant(const char* dex_location,
-                   int target_compilation_type_mask,
                    const InstructionSet isa,
+                   bool profile_changed,
                    bool load_executable);
 
   // Constructs an OatFileAssistant, providing an explicit target oat_location
   // to use instead of the standard oat location.
   OatFileAssistant(const char* dex_location,
                    const char* oat_location,
-                   int target_compilation_type_mask,
                    const InstructionSet isa,
+                   bool profile_changed,
                    bool load_executable);
 
   ~OatFileAssistant();
@@ -158,16 +144,18 @@
   bool Lock(std::string* error_msg);
 
   // Return what action needs to be taken to produce up-to-date code for this
-  // dex location.
-  DexOptNeeded GetDexOptNeeded();
+  // dex location that is at least as good as an oat file generated with the
+  // given compiler filter.
+  DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter);
 
   // Attempts to generate or relocate the oat file as needed to make it up to
-  // date.
+  // date with in a way that is at least as good as an oat file generated with
+  // the given compiler filter.
   // Returns true on success.
   //
   // If there is a failure, the value of error_msg will be set to a string
   // describing why there was failure. error_msg must not be null.
-  bool MakeUpToDate(std::string* error_msg);
+  bool MakeUpToDate(CompilerFilter::Filter target_compiler_filter, std::string* error_msg);
 
   // Returns an oat file that can be used for loading dex files.
   // Returns null if no suitable oat file was found.
@@ -251,7 +239,7 @@
   // describing why there was failure. error_msg must not be null.
   bool RelocateOatFile(const std::string* input_file, std::string* error_msg);
 
-  // Generate the oat file from the dex file.
+  // Generate the oat file from the dex file using the given compiler filter.
   // This does not check the current status before attempting to generate the
   // oat file.
   // Returns true on success.
@@ -259,7 +247,7 @@
   //
   // If there is a failure, the value of error_msg will be set to a string
   // describing why there was failure. error_msg must not be null.
-  bool GenerateOatFile(std::string* error_msg);
+  bool GenerateOatFile(CompilerFilter::Filter filter, std::string* error_msg);
 
   // Executes dex2oat using the current runtime configuration overridden with
   // the given arguments. This does not check to see if dex2oat is enabled in
@@ -315,6 +303,10 @@
   // The caller shouldn't clean up or free the returned pointer.
   const OatFile* GetOdexFile();
 
+  // Returns true if the compiler filter used to generate the odex file is at
+  // least as good as the given target filter.
+  bool OdexFileCompilerFilterIsOkay(CompilerFilter::Filter target);
+
   // Returns true if the odex file is opened executable.
   bool OdexFileIsExecutable();
 
@@ -327,6 +319,10 @@
   // The caller shouldn't clean up or free the returned pointer.
   const OatFile* GetOatFile();
 
+  // Returns true if the compiler filter used to generate the oat file is at
+  // least as good as the given target filter.
+  bool OatFileCompilerFilterIsOkay(CompilerFilter::Filter target);
+
   // Returns true if the oat file is opened executable.
   bool OatFileIsExecutable();
 
@@ -346,12 +342,14 @@
   ScopedFlock flock_;
 
   std::string dex_location_;
-  const int target_compilation_type_mask_;
 
   // In a properly constructed OatFileAssistant object, isa_ should be either
   // the 32 or 64 bit variant for the current device.
   const InstructionSet isa_ = kNone;
 
+  // Whether the profile has recently changed.
+  bool profile_changed_ = false;
+
   // Whether we will attempt to load oat files executable.
   bool load_executable_ = false;
 
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 046d8ae..634e048 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -188,7 +188,8 @@
   // Generate a non-PIC odex file for the purposes of test.
   // The generated odex file will be un-relocated.
   void GenerateOdexForTest(const std::string& dex_location,
-                           const std::string& odex_location) {
+                           const std::string& odex_location,
+                           CompilerFilter::Filter filter) {
     // To generate an un-relocated odex file, we first compile a relocated
     // version of the file, then manually call patchoat to make it look as if
     // it is unrelocated.
@@ -196,11 +197,12 @@
     std::vector<std::string> args;
     args.push_back("--dex-file=" + dex_location);
     args.push_back("--oat-file=" + relocated_odex_location);
-    args.push_back("--include-patch-information");
+    args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
 
     // We need to use the quick compiler to generate non-PIC code, because
     // the optimizing compiler always generates PIC.
     args.push_back("--compiler-backend=Quick");
+    args.push_back("--include-patch-information");
 
     std::string error_msg;
     ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
@@ -227,21 +229,25 @@
                                                      dex_location.c_str(),
                                                      &error_msg));
     ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
-
-    const std::vector<gc::space::ImageSpace*> image_spaces =
-        runtime->GetHeap()->GetBootImageSpaces();
-    ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr);
-    const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
-    const OatHeader& oat_header = odex_file->GetOatHeader();
     EXPECT_FALSE(odex_file->IsPic());
-    EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum());
-    EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
-        oat_header.GetImageFileLocationOatDataBegin());
-    EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta());
+    EXPECT_EQ(filter, odex_file->GetCompilerFilter());
+
+    if (CompilerFilter::IsCompilationEnabled(filter)) {
+      const std::vector<gc::space::ImageSpace*> image_spaces =
+        runtime->GetHeap()->GetBootImageSpaces();
+      ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr);
+      const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
+      const OatHeader& oat_header = odex_file->GetOatHeader();
+      EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum());
+      EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
+          oat_header.GetImageFileLocationOatDataBegin());
+      EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta());
+    }
   }
 
   void GeneratePicOdexForTest(const std::string& dex_location,
-                              const std::string& odex_location) {
+                              const std::string& odex_location,
+                              CompilerFilter::Filter filter) {
     // Temporarily redirect the dalvik cache so dex2oat doesn't find the
     // relocated image file.
     std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp";
@@ -249,6 +255,7 @@
     std::vector<std::string> args;
     args.push_back("--dex-file=" + dex_location);
     args.push_back("--oat-file=" + odex_location);
+    args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
     args.push_back("--compile-pic");
     args.push_back("--runtime-arg");
     args.push_back("-Xnorelocate");
@@ -267,55 +274,7 @@
                                                      &error_msg));
     ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
     EXPECT_TRUE(odex_file->IsPic());
-  }
-
-  void GenerateExtractOnlyOdexForTest(const std::string& dex_location,
-                                      const std::string& odex_location) {
-    std::vector<std::string> args;
-    args.push_back("--dex-file=" + dex_location);
-    args.push_back("--oat-file=" + odex_location);
-    args.push_back("--compiler-filter=verify-at-runtime");
-    std::string error_msg;
-    ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
-    // Verify the odex file was generated as expected.
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
-                                                     odex_location.c_str(),
-                                                     nullptr,
-                                                     nullptr,
-                                                     false,
-                                                     /*low_4gb*/false,
-                                                     dex_location.c_str(),
-                                                     &error_msg));
-    ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
-    EXPECT_TRUE(odex_file->IsExtractOnly());
-    EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u);
-    EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0u);
-    EXPECT_EQ(odex_file->GetOatHeader().GetImagePatchDelta(), 0);
-  }
-
-  void GenerateProfileGuideOdexForTest(const std::string& dex_location,
-                                       const std::string& odex_location) {
-    std::vector<std::string> args;
-    args.push_back("--dex-file=" + dex_location);
-    args.push_back("--oat-file=" + odex_location);
-    ScratchFile profile_file;
-    args.push_back("--profile-file=" + profile_file.GetFilename());
-    std::string error_msg;
-    ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
-    // Verify the odex file was generated as expected.
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
-                                                     odex_location.c_str(),
-                                                     nullptr,
-                                                     nullptr,
-                                                     false,
-                                                     /*low_4gb*/false,
-                                                     dex_location.c_str(),
-                                                     &error_msg));
-    printf("error %s", error_msg.c_str());
-    ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
-    EXPECT_TRUE(odex_file->IsProfileGuideCompiled());
+    EXPECT_EQ(filter, odex_file->GetCompilerFilter());
   }
 
  private:
@@ -382,12 +341,32 @@
 
 // Generate an oat file for the purposes of test, as opposed to testing
 // generation of oat files.
-static void GenerateOatForTest(const char* dex_location) {
-  OatFileAssistant oat_file_assistant(dex_location,
-      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+static void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
+  // Use an oat file assistant to find the proper oat location.
+  OatFileAssistant ofa(dex_location, kRuntimeISA, false, false);
+  const std::string* oat_location = ofa.OatFileName();
+  ASSERT_TRUE(oat_location != nullptr);
 
+  std::vector<std::string> args;
+  args.push_back("--dex-file=" + std::string(dex_location));
+  args.push_back("--oat-file=" + *oat_location);
+  args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
+  args.push_back("--runtime-arg");
+  args.push_back("-Xnorelocate");
   std::string error_msg;
-  ASSERT_TRUE(oat_file_assistant.GenerateOatFile(&error_msg)) << error_msg;
+  ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+  // Verify the oat file was generated as expected.
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location->c_str(),
+                                                  oat_location->c_str(),
+                                                  nullptr,
+                                                  nullptr,
+                                                  false,
+                                                  /*low_4gb*/false,
+                                                  dex_location,
+                                                  &error_msg));
+  ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
+  EXPECT_EQ(filter, oat_file->GetCompilerFilter());
 }
 
 // Case: We have a DEX file, but no OAT file for it.
@@ -396,10 +375,16 @@
   std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
   Copy(GetDexSrc1(), dex_location);
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
@@ -420,15 +405,15 @@
 TEST_F(OatFileAssistantTest, NoDexNoOat) {
   std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
 
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Trying to make the oat file up to date should not fail or crash.
   std::string error_msg;
-  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg));
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg));
 
   // Trying to get the best oat file should fail, but not crash.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -440,12 +425,19 @@
 TEST_F(OatFileAssistantTest, OatUpToDate) {
   std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
   Copy(GetDexSrc1(), dex_location);
-  GenerateOatForTest(dex_location.c_str());
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false);
 
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
+
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
   EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
@@ -458,16 +450,68 @@
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
+// Case: We have a DEX file and speed-profile OAT file for it.
+// Expect: The status is kNoDexOptNeeded if the profile hasn't changed.
+TEST_F(OatFileAssistantTest, ProfileOatUpToDate) {
+  std::string dex_location = GetScratchDir() + "/ProfileOatUpToDate.jar";
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeedProfile);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false);
+
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileExists());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
+// Case: We have a DEX file and speed-profile OAT file for it.
+// Expect: The status is kNoDex2OatNeeded if the profile has changed.
+TEST_F(OatFileAssistantTest, ProfileOatOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/ProfileOatOutOfDate.jar";
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeedProfile);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true, false);
+
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
 // Case: We have a MultiDEX file and up-to-date OAT file for it.
 // Expect: The status is kNoDexOptNeeded and we load all dex files.
 TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
   std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
   Copy(GetMultiDexSrc1(), dex_location);
-  GenerateOatForTest(dex_location.c_str());
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load both dex files.
@@ -486,15 +530,15 @@
 
   // Compile code for GetMultiDexSrc1.
   Copy(GetMultiDexSrc1(), dex_location);
-  GenerateOatForTest(dex_location.c_str());
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
 
   // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
   // is out of date.
   Copy(GetMultiDexSrc2(), dex_location);
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -513,6 +557,7 @@
   args.push_back("--dex-file=" + dex_location);
   args.push_back("--dex-location=" + std::string("RelativeEncodedDexLocation.jar"));
   args.push_back("--oat-file=" + oat_location);
+  args.push_back("--compiler-filter=speed");
 
   std::string error_msg;
   ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
@@ -520,8 +565,7 @@
   // Verify we can load both dex files.
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       oat_location.c_str(),
-                                      OatFileAssistant::kFullCompilation,
-                                      kRuntimeISA, true);
+                                      kRuntimeISA, false, true);
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
   EXPECT_TRUE(oat_file->IsExecutable());
@@ -538,12 +582,14 @@
   // We create a dex, generate an oat for it, then overwrite the dex with a
   // different dex to make the oat out of date.
   Copy(GetDexSrc1(), dex_location);
-  GenerateOatForTest(dex_location.c_str());
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
   Copy(GetDexSrc2(), dex_location);
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false);
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
@@ -563,13 +609,15 @@
 
   // Create the dex and odex files
   Copy(GetDexSrc1(), dex_location);
-  GenerateOdexForTest(dex_location, odex_location);
+  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false);
 
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -594,16 +642,16 @@
 
   // Create the dex and odex files
   Copy(GetDexSrc1(), dex_location);
-  GenerateOdexForTest(dex_location, odex_location);
+  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
 
   // Strip the dex file
   Copy(GetStrippedDexSrc1(), dex_location);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
 
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -616,9 +664,10 @@
 
   // Make the oat file up to date.
   std::string error_msg;
-  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
 
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -646,20 +695,24 @@
 
   // Create the oat file from a different dex file so it looks out of date.
   Copy(GetDexSrc2(), dex_location);
-  GenerateOatForTest(dex_location.c_str());
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
 
   // Create the odex file
   Copy(GetDexSrc1(), dex_location);
-  GenerateOdexForTest(dex_location, odex_location);
+  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
 
   // Strip the dex file.
   Copy(GetStrippedDexSrc1(), dex_location);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
 
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,  // Can't run dex2oat because dex file is stripped.
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -673,9 +726,12 @@
 
   // Make the oat file up to date.
   std::string error_msg;
-  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
 
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,  // Can't run dex2oat because dex file is stripped.
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -705,10 +761,14 @@
   Copy(GetStrippedDexSrc1(), dex_location);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
 
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
@@ -722,9 +782,10 @@
 
   // Make the oat file up to date. This should have no effect.
   std::string error_msg;
-  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
 
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
@@ -746,12 +807,17 @@
 
   // Create the dex and odex files
   Copy(GetDexSrc1(), dex_location);
-  GenerateOdexForTest(dex_location, oat_location);
+  GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+      oat_location.c_str(), kRuntimeISA, false, true);
 
-  EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+  EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
@@ -766,9 +832,10 @@
 
   // Make the oat file up to date.
   std::string error_msg;
-  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
 
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
@@ -799,7 +866,7 @@
 
   // Create the dex and odex files
   Copy(GetDexSrc1(), dex_location);
-  GenerateOdexForTest(dex_location, odex_location);
+  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
 
   // Create the oat file by copying the odex so they are located in the same
   // place in memory.
@@ -807,9 +874,10 @@
 
   // Verify things don't go bad.
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+      oat_location.c_str(), kRuntimeISA, false, true);
 
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -838,13 +906,15 @@
 
   // Create the dex and odex files
   Copy(GetDexSrc1(), dex_location);
-  GeneratePicOdexForTest(dex_location, odex_location);
+  GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false);
 
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -856,22 +926,23 @@
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
-// Case: We have a DEX file and a ExtractOnly ODEX file, but no OAT file.
-// Expect: The status is kNoDexOptNeeded, because ExtractOnly contains no code.
-TEST_F(OatFileAssistantTest, DexExtractOnlyOdexNoOat) {
-  std::string dex_location = GetScratchDir() + "/DexExtractOnlyOdexNoOat.jar";
-  std::string odex_location = GetOdexDir() + "/DexExtractOnlyOdexNoOat.odex";
+// Case: We have a DEX file and a VerifyAtRuntime ODEX file, but no OAT file.
+// Expect: The status is kNoDexOptNeeded, because VerifyAtRuntime contains no code.
+TEST_F(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) {
+  std::string dex_location = GetScratchDir() + "/DexVerifyAtRuntimeOdexNoOat.jar";
+  std::string odex_location = GetOdexDir() + "/DexVerifyAtRuntimeOdexNoOat.odex";
 
   // Create the dex and odex files
   Copy(GetDexSrc1(), dex_location);
-  GenerateExtractOnlyOdexForTest(dex_location, odex_location);
+  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerifyAtRuntime);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly,
-      kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false);
 
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -889,11 +960,29 @@
   std::string dex_location = GetScratchDir() + "/LoadOatUpToDate.jar";
 
   Copy(GetDexSrc1(), dex_location);
-  GenerateOatForTest(dex_location.c_str());
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
 
   // Load the oat using an oat file assistant.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_TRUE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a DEX file and up-to-date interpret-only OAT file for it.
+// Expect: We should still load the oat file as executable.
+TEST_F(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) {
+  std::string dex_location = GetScratchDir() + "/LoadExecInterpretOnlyOatUpToDate.jar";
+
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kInterpretOnly);
+
+  // Load the oat using an oat file assistant.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -909,11 +998,10 @@
   std::string dex_location = GetScratchDir() + "/LoadNoExecOatUpToDate.jar";
 
   Copy(GetDexSrc1(), dex_location);
-  GenerateOatForTest(dex_location.c_str());
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
 
   // Load the oat using an oat file assistant.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false);
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -933,10 +1021,9 @@
   Copy(GetDexSrc1(), dex_location);
 
   OatFileAssistant oat_file_assistant(
-      dex_location.c_str(), oat_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+      dex_location.c_str(), oat_location.c_str(), kRuntimeISA, false, true);
   std::string error_msg;
-  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -948,8 +1035,7 @@
   EXPECT_TRUE(OS::FileExists(oat_location.c_str()));
 
   // Verify it didn't create an oat in the default location.
-  OatFileAssistant ofm(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+  OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false, false);
   EXPECT_FALSE(ofm.OatFileExists());
 }
 
@@ -965,10 +1051,9 @@
   Copy(GetDexSrc1(), dex_location);
 
   OatFileAssistant oat_file_assistant(
-      dex_location.c_str(), oat_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+      dex_location.c_str(), oat_location.c_str(), kRuntimeISA, false, true);
   std::string error_msg;
-  ASSERT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
+  ASSERT_FALSE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg));
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() == nullptr);
@@ -981,10 +1066,9 @@
   std::string oat_location = GetScratchDir() + "/GenNoDex.oat";
 
   OatFileAssistant oat_file_assistant(
-      dex_location.c_str(), oat_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+      dex_location.c_str(), oat_location.c_str(), kRuntimeISA, false, true);
   std::string error_msg;
-  ASSERT_FALSE(oat_file_assistant.GenerateOatFile(&error_msg));
+  ASSERT_FALSE(oat_file_assistant.GenerateOatFile(CompilerFilter::kSpeed, &error_msg));
 }
 
 // Turn an absolute path into a path relative to the current working
@@ -1030,11 +1114,11 @@
   Copy(GetDexSrc1(), abs_dex_location);
 
   std::string dex_location = MakePathRelative(abs_dex_location);
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
@@ -1048,11 +1132,11 @@
 TEST_F(OatFileAssistantTest, ShortDexLocation) {
   std::string dex_location = "/xx";
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
@@ -1063,7 +1147,7 @@
 
   // Trying to make it up to date should have no effect.
   std::string error_msg;
-  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg));
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg));
   EXPECT_TRUE(error_msg.empty());
 }
 
@@ -1073,10 +1157,10 @@
   std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
   Copy(GetDexSrc1(), dex_location);
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
@@ -1168,11 +1252,10 @@
 
   // Create the dex and odex files
   Copy(GetDexSrc1(), dex_location);
-  GenerateOdexForTest(dex_location, odex_location);
+  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
 
   // Load the oat using an executable oat file assistant.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -1191,11 +1274,10 @@
 
   // Create the dex and odex files
   Copy(GetMultiDexSrc1(), dex_location);
-  GenerateOdexForTest(dex_location, odex_location);
+  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
 
   // Load the oat using an executable oat file assistant.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true);
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -1223,45 +1305,6 @@
         "/foo/bar/baz_noext", kArm, &odex_file, &error_msg));
 }
 
-// Case: We have a DEX file, extract-only ODEX, and fully compiled OAT.
-// Expect: The status depends on the target compilation type mask.
-TEST_F(OatFileAssistantTest, TargetCompilationType) {
-  std::string dex_location = GetScratchDir() + "/TargetCompilationType.jar";
-  std::string odex_location = GetOdexDir() + "/TargetCompilationType.odex";
-  Copy(GetDexSrc1(), dex_location);
-  GenerateExtractOnlyOdexForTest(dex_location, odex_location);
-  GenerateOatForTest(dex_location.c_str());
-
-  OatFileAssistant ofa_full(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_full.GetDexOptNeeded());
-  EXPECT_FALSE(ofa_full.IsInBootClassPath());
-  EXPECT_TRUE(ofa_full.OdexFileIsOutOfDate());
-  EXPECT_TRUE(ofa_full.OatFileIsUpToDate());
-
-  OatFileAssistant ofa_extract(dex_location.c_str(),
-      OatFileAssistant::kExtractOnly, kRuntimeISA, false);
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract.GetDexOptNeeded());
-  EXPECT_FALSE(ofa_extract.IsInBootClassPath());
-  EXPECT_TRUE(ofa_extract.OdexFileIsUpToDate());
-  EXPECT_TRUE(ofa_extract.OatFileIsOutOfDate());
-
-  OatFileAssistant ofa_profile(dex_location.c_str(),
-      OatFileAssistant::kProfileGuideCompilation, kRuntimeISA, false);
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, ofa_profile.GetDexOptNeeded());
-  EXPECT_FALSE(ofa_profile.IsInBootClassPath());
-  EXPECT_TRUE(ofa_profile.OdexFileIsOutOfDate());
-  EXPECT_TRUE(ofa_profile.OatFileIsOutOfDate());
-
-  OatFileAssistant ofa_extract_full(dex_location.c_str(),
-      OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly,
-      kRuntimeISA, false);
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract_full.GetDexOptNeeded());
-  EXPECT_FALSE(ofa_extract_full.IsInBootClassPath());
-  EXPECT_TRUE(ofa_extract_full.OdexFileIsUpToDate());
-  EXPECT_TRUE(ofa_extract_full.OatFileIsUpToDate());
-}
-
 // Verify the dexopt status values from dalvik.system.DexFile
 // match the OatFileAssistant::DexOptStatus values.
 TEST_F(OatFileAssistantTest, DexOptStatusValues) {
@@ -1296,28 +1339,12 @@
   ASSERT_FALSE(self_patchoat_needed == nullptr);
   EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
   EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get()));
-
-    ArtField* compilation_type_full = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "COMPILATION_TYPE_FULL", "I");
-  ASSERT_FALSE(compilation_type_full == nullptr);
-  EXPECT_EQ(compilation_type_full->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kFullCompilation, compilation_type_full->GetInt(dexfile.Get()));
-
-  ArtField* compilation_type_profile_guide = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "COMPILATION_TYPE_PROFILE_GUIDE", "I");
-  ASSERT_FALSE(compilation_type_profile_guide == nullptr);
-  EXPECT_EQ(compilation_type_profile_guide->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kProfileGuideCompilation,
-            compilation_type_profile_guide->GetInt(dexfile.Get()));
-
-  ArtField* compilation_type_extract_only = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "COMPILATION_TYPE_EXTRACT_ONLY", "I");
-  ASSERT_FALSE(compilation_type_extract_only == nullptr);
-  EXPECT_EQ(compilation_type_extract_only->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kExtractOnly, compilation_type_extract_only->GetInt(dexfile.Get()));
 }
 
 // TODO: More Tests:
+//  * Image checksum change is out of date for kIntepretOnly, but not
+//    kVerifyAtRuntime. But target of kVerifyAtRuntime still says current
+//    kInterpretOnly is out of date.
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
 //  * Test using secondary isa
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 3ec3826..2f13f55 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -44,6 +44,8 @@
 // If true, then we attempt to load the application image if it exists.
 static constexpr bool kEnableAppImage = true;
 
+CompilerFilter::Filter OatFileManager::filter_ = CompilerFilter::Filter::kSpeed;
+
 const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) {
   WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
   DCHECK(oat_file != nullptr);
@@ -308,13 +310,10 @@
   Locks::mutator_lock_->AssertNotHeld(self);
   Runtime* const runtime = Runtime::Current();
 
-  int target_compilation_type_mask = OatFileAssistant::kFullCompilation
-    | OatFileAssistant::kProfileGuideCompilation
-    | OatFileAssistant::kExtractOnly;
   OatFileAssistant oat_file_assistant(dex_location,
                                       oat_location,
-                                      target_compilation_type_mask,
                                       kRuntimeISA,
+                                      /*profile_changed*/false,
                                       !runtime->IsAotCompiler());
 
   // Lock the target oat location to avoid races generating and loading the
@@ -330,7 +329,7 @@
 
   // Update the oat file on disk if we can. This may fail, but that's okay.
   // Best effort is all that matters here.
-  if (!oat_file_assistant.MakeUpToDate(/*out*/&error_msg)) {
+  if (!oat_file_assistant.MakeUpToDate(filter_, /*out*/ &error_msg)) {
     LOG(INFO) << error_msg;
   }
 
@@ -484,15 +483,7 @@
     if (ContainsElement(boot_oat_files, oat_file.get())) {
       continue;
     }
-    // Use "platform-default" if it's neither extract nor profile guided.
-    // Saying 'full' could be misleading if for example the platform uses
-    // compiler filters.
-    const char* status = oat_file->IsExtractOnly()
-        ? OatHeader::kExtractOnlyValue
-        : oat_file->IsProfileGuideCompiled()
-            ? OatHeader::kProfileGuideCompiledValue
-            : "platform-default";
-    os << oat_file->GetLocation() << ": " << status << "\n";
+    os << oat_file->GetLocation() << ": " << oat_file->GetCompilerFilter() << "\n";
   }
 }
 
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index a541d10..574d0e2 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -25,6 +25,7 @@
 
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "compiler_filter.h"
 #include "jni.h"
 
 namespace art {
@@ -110,6 +111,10 @@
 
   void DumpForSigQuit(std::ostream& os);
 
+  static void SetCompilerFilter(CompilerFilter::Filter filter) {
+    filter_ = filter;
+  }
+
  private:
   // Check for duplicate class definitions of the given oat file against all open oat files.
   // Return true if there are any class definition collisions in the oat_file.
@@ -122,6 +127,10 @@
   std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
   std::unordered_map<std::string, size_t> oat_file_count_ GUARDED_BY(Locks::oat_file_count_lock_);
   bool have_non_pic_oat_file_;
+
+  // The compiler filter used for oat files loaded by the oat file manager.
+  static CompilerFilter::Filter filter_;
+
   DISALLOW_COPY_AND_ASSIGN(OatFileManager);
 };
 
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 60403f9..48c91f6 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -286,6 +286,9 @@
           .IntoKey(M::Experimental)
       .Define("-Xforce-nb-testing")
           .IntoKey(M::ForceNativeBridge)
+      .Define("-XOatFileManagerCompilerFilter:_")
+          .WithType<std::string>()
+          .IntoKey(M::OatFileManagerCompilerFilter)
       .Ignore({
           "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
           "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 941217f..96f41b3 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -60,6 +60,7 @@
 #include "base/unix_file/fd_file.h"
 #include "class_linker-inl.h"
 #include "compiler_callbacks.h"
+#include "compiler_filter.h"
 #include "debugger.h"
 #include "elf_file.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
@@ -959,6 +960,16 @@
   experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental);
   is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);
 
+  {
+    CompilerFilter::Filter filter;
+    std::string filter_str = runtime_options.GetOrDefault(Opt::OatFileManagerCompilerFilter);
+    if (!CompilerFilter::ParseCompilerFilter(filter_str.c_str(), &filter)) {
+      LOG(ERROR) << "Cannot parse compiler filter " << filter_str;
+      return false;
+    }
+    OatFileManager::SetCompilerFilter(filter);
+  }
+
   XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
   heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
                        runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
@@ -1606,8 +1617,8 @@
     method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
   } else {
     method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictStub());
+    method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
   }
-  method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
   return method;
 }
 
@@ -1615,7 +1626,9 @@
   CHECK(method != nullptr);
   CHECK(method->IsRuntimeMethod());
   imt_conflict_method_ = method;
-  method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
+  if (!IsAotCompiler()) {
+    method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
+  }
 }
 
 ArtMethod* Runtime::CreateResolutionMethod() {
@@ -1933,7 +1946,9 @@
   CHECK(method != nullptr);
   CHECK(method->IsRuntimeMethod());
   imt_unimplemented_method_ = method;
-  method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
+  if (!IsAotCompiler()) {
+    method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
+  }
 }
 
 bool Runtime::IsVerificationEnabled() const {
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 3fd9905..6a50ffa 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -132,5 +132,6 @@
                                                                           // We don't call abort(3) by default; see
                                                                           // Runtime::Abort.
 RUNTIME_OPTIONS_KEY (void (*)(),          HookAbort,                      nullptr)
+RUNTIME_OPTIONS_KEY (std::string,         OatFileManagerCompilerFilter,   "speed")
 
 #undef RUNTIME_OPTIONS_KEY
diff --git a/test/004-checker-UnsafeTest18/src/Main.java b/test/004-checker-UnsafeTest18/src/Main.java
index c50613d..282f9ce 100644
--- a/test/004-checker-UnsafeTest18/src/Main.java
+++ b/test/004-checker-UnsafeTest18/src/Main.java
@@ -133,6 +133,10 @@
   private static void fork(Runnable r) {
     for (int i = 0; i < 10; i++) {
       sThreads[i] = new Thread(r);
+    }
+    // Start the threads only after the full array has been written with new threads,
+    // because one test relies on the contents of this array to be consistent.
+    for (int i = 0; i < 10; i++) {
       sThreads[i].start();
     }
   }
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc
index 82e1fc8..0dab400 100644
--- a/test/117-nopatchoat/nopatchoat.cc
+++ b/test/117-nopatchoat/nopatchoat.cc
@@ -54,7 +54,8 @@
     }
 
     const OatFile* oat_file = oat_dex_file->GetOatFile();
-    return !oat_file->IsPic() && !oat_file->IsExtractOnly();
+    return !oat_file->IsPic()
+        && CompilerFilter::IsCompilationEnabled(oat_file->GetCompilerFilter());
   }
 };
 
diff --git a/test/590-checker-array-set-null-regression/expected.txt b/test/590-checker-array-set-null-regression/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/590-checker-array-set-null-regression/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/590-checker-array-set-null-regression/info.txt b/test/590-checker-array-set-null-regression/info.txt
new file mode 100644
index 0000000..fe173a3
--- /dev/null
+++ b/test/590-checker-array-set-null-regression/info.txt
@@ -0,0 +1,11 @@
+Regression test for art::PrepareForRegisterAllocation, which replaces
+
+  ArraySet[array, index, BoundType[NullConstant]]
+
+with
+
+  ArraySet[array, index, NullConstant]
+
+but used to forget to remove the "need for a type check" bit in the
+ArraySet, thus failing "!may_need_runtime_call_for_type_check"
+assertions in code generators.
diff --git a/test/590-checker-array-set-null-regression/src/Main.java b/test/590-checker-array-set-null-regression/src/Main.java
new file mode 100644
index 0000000..792ee4e
--- /dev/null
+++ b/test/590-checker-array-set-null-regression/src/Main.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  public static void main(String args[]) {
+    Element[] elements = new Element[51];
+    testArraySetCheckCastNull(elements);
+
+    System.out.println("passed");
+  }
+
+  /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) builder (after)
+  /// CHECK:         <<Array:l\d+>>         ParameterValue
+  /// CHECK-DAG:     <<Index:i\d+>>         IntConstant 42
+  /// CHECK-DAG:     <<Null:l\d+>>          NullConstant
+  /// CHECK-DAG:     <<Class:l\d+>>         LoadClass
+  /// CHECK-DAG:                            CheckCast [<<Null>>,<<Class>>]
+  /// CHECK-DAG:     <<CheckedValue:l\d+>>  BoundType [<<Null>>] klass:Main$Element can_be_null:true
+  /// CHECK-DAG:     <<CheckedArray:l\d+>>  NullCheck [<<Array>>]
+  /// CHECK-DAG:     <<Length:i\d+>>        ArrayLength [<<CheckedArray>>]
+  /// CHECK-DAG:     <<CheckedIndex:i\d+>>  BoundsCheck [<<Index>>,<<Length>>]
+  /// CHECK-DAG:     <<ArraySet:v\d+>>      ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true
+
+  /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) instruction_simplifier (after)
+  /// CHECK-NOT:                            CheckCast
+
+  /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) prepare_for_register_allocation (before)
+  /// CHECK:         <<Array:l\d+>>         ParameterValue
+  /// CHECK-DAG:     <<Index:i\d+>>         IntConstant 42
+  /// CHECK-DAG:     <<Null:l\d+>>          NullConstant
+  /// CHECK-DAG:     <<Class:l\d+>>         LoadClass
+  /// CHECK-DAG:     <<CheckedValue:l\d+>>  BoundType [<<Null>>]
+  /// CHECK-DAG:     <<CheckedArray:l\d+>>  NullCheck [<<Array>>]
+  /// CHECK-DAG:     <<Length:i\d+>>        ArrayLength [<<CheckedArray>>]
+  /// CHECK-DAG:     <<CheckedIndex:i\d+>>  BoundsCheck [<<Index>>,<<Length>>]
+  /// CHECK-DAG:     <<ArraySet:v\d+>>      ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true
+
+  /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) prepare_for_register_allocation (after)
+  /// CHECK:         <<Array:l\d+>>         ParameterValue
+  /// CHECK-DAG:     <<Index:i\d+>>         IntConstant 42
+  /// CHECK-DAG:     <<Null:l\d+>>          NullConstant
+  /// CHECK-DAG:     <<Class:l\d+>>         LoadClass
+  /// CHECK-DAG:     <<Length:i\d+>>        ArrayLength [<<Array>>]
+  /// CHECK-DAG:     <<ArraySet:v\d+>>      ArraySet [<<Array>>,<<Index>>,<<Null>>] needs_type_check:false
+
+  static void testArraySetCheckCastNull(Element[] elements) {
+    Object object = null;
+    Element element = (Element) object;
+    elements[42] = element;
+  }
+
+  class Element {}
+
+}
diff --git a/test/590-infinite-loop-with-nop/expected.txt b/test/590-infinite-loop-with-nop/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/590-infinite-loop-with-nop/expected.txt
diff --git a/test/590-infinite-loop-with-nop/info.txt b/test/590-infinite-loop-with-nop/info.txt
new file mode 100644
index 0000000..ee09709
--- /dev/null
+++ b/test/590-infinite-loop-with-nop/info.txt
@@ -0,0 +1 @@
+Regression test for debug build check failure for infinite loop with NOP.
diff --git a/test/590-infinite-loop-with-nop/smali/TestCase.smali b/test/590-infinite-loop-with-nop/smali/TestCase.smali
new file mode 100644
index 0000000..7ea495d
--- /dev/null
+++ b/test/590-infinite-loop-with-nop/smali/TestCase.smali
@@ -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 LTestCase;
+
+.super Ljava/lang/Object;
+
+.method static public infiniteLoop()V
+  .registers 0
+  :infinite_loop
+  nop
+  goto :infinite_loop
+.end method
+
+# Add a field to work around
+#   Failure to verify dex file '...': Offset(208) should be zero when size is zero for field-ids.
+.field private a:I
diff --git a/test/590-infinite-loop-with-nop/src/Main.java b/test/590-infinite-loop-with-nop/src/Main.java
new file mode 100644
index 0000000..531ff28
--- /dev/null
+++ b/test/590-infinite-loop-with-nop/src/Main.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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+  }
+}
+
diff --git a/test/run-test b/test/run-test
index 55989c6..01464cd 100755
--- a/test/run-test
+++ b/test/run-test
@@ -241,11 +241,11 @@
         run_args="${run_args} --zygote"
         shift
     elif [ "x$1" = "x--interpreter" ]; then
-        run_args="${run_args} --interpreter"
+        run_args="${run_args} --interpreter --runtime-option -XOatFileManagerCompilerFilter:verify-at-runtime"
         image_suffix="-interpreter"
         shift
     elif [ "x$1" = "x--jit" ]; then
-        run_args="${run_args} --jit"
+        run_args="${run_args} --jit --runtime-option -XOatFileManagerCompilerFilter:verify-at-runtime"
         image_suffix="-jit"
         shift
     elif [ "x$1" = "x--optimizing" ]; then
@@ -253,10 +253,10 @@
         image_suffix="-optimizing"
         shift
     elif [ "x$1" = "x--no-verify" ]; then
-        run_args="${run_args} --no-verify"
+        run_args="${run_args} --no-verify --runtime-option -XOatFileManagerCompilerFilter:verify-none"
         shift
     elif [ "x$1" = "x--verify-soft-fail" ]; then
-        run_args="${run_args} --verify-soft-fail"
+        run_args="${run_args} --verify-soft-fail --runtime-option -XOatFileManagerCompilerFilter:verify-at-runtime"
         image_suffix="-interp-ac"
         shift
     elif [ "x$1" = "x--no-optimize" ]; then