Merge "ART: checkpoint mechanism optimization"
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 1f040d6..0f756ef 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -20,6 +20,21 @@
 ART_TARGET_SUPPORTED_ARCH := arm arm64 mips mips64 x86 x86_64
 ART_HOST_SUPPORTED_ARCH := x86 x86_64
 
+ART_COVERAGE := false
+
+ifeq ($(ART_COVERAGE),true)
+# https://gcc.gnu.org/onlinedocs/gcc/Cross-profiling.html
+GCOV_PREFIX := /data/local/tmp/gcov
+# GCOV_PREFIX_STRIP is an integer that defines how many levels should be
+# stripped off the beginning of the path. We want the paths in $GCOV_PREFIX to
+# be relative to $ANDROID_BUILD_TOP so we can just adb pull from the top and not
+# have to worry about placing things ourselves.
+GCOV_PREFIX_STRIP := $(shell echo $(ANDROID_BUILD_TOP) | grep -o / | wc -l)
+GCOV_ENV := GCOV_PREFIX=$(GCOV_PREFIX) GCOV_PREFIX_STRIP=$(GCOV_PREFIX_STRIP)
+else
+GCOV_ENV :=
+endif
+
 ifeq (,$(filter $(TARGET_ARCH),$(ART_TARGET_SUPPORTED_ARCH)))
 $(warning unsupported TARGET_ARCH=$(TARGET_ARCH))
 endif
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index ad4c987..3000cdf 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -238,10 +238,14 @@
 
 ifeq ($(HOST_OS),linux)
   # Larger frame-size for host clang builds today
-  ifndef SANITIZE_HOST
-    art_host_non_debug_cflags += -Wframe-larger-than=2700
+  ifneq ($(ART_COVERAGE),true)
+    ifneq ($(NATIVE_COVERAGE),true)
+      ifndef SANITIZE_HOST
+        art_host_non_debug_cflags += -Wframe-larger-than=2700
+      endif
+      art_target_non_debug_cflags += -Wframe-larger-than=1728
+    endif
   endif
-  art_target_non_debug_cflags += -Wframe-larger-than=1728
 endif
 
 ifndef LIBART_IMG_HOST_BASE_ADDRESS
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index 4bb2cb1..dfea6e1 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -129,6 +129,8 @@
     endif
   endif
 
+  LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
   ifeq ($$(art_target_or_host),target)
     include $(BUILD_EXECUTABLE)
     ART_TARGET_EXECUTABLES := $(ART_TARGET_EXECUTABLES) $$(foreach name,$$(art_out_binary_name),$(TARGET_OUT_EXECUTABLES)/$$(name))
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index a5cfcba..b4eb0c0 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -275,7 +275,7 @@
 	$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID
 	$(hide) adb shell chmod 755 $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1)
 	$(hide) $$(call ART_TEST_SKIP,$$@) && \
-	  (adb shell "LD_LIBRARY_PATH=$(3) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
+	  (adb shell "$(GCOV_ENV) LD_LIBRARY_PATH=$(3) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
 	    $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID" \
 	  && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID /tmp/ \
 	      && $$(call ART_TEST_PASSED,$$@)) \
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 83ab730..b87201a 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -100,8 +100,9 @@
 	optimizing/inliner.cc \
 	optimizing/instruction_simplifier.cc \
 	optimizing/intrinsics.cc \
-	optimizing/intrinsics_x86_64.cc \
 	optimizing/intrinsics_arm64.cc \
+	optimizing/intrinsics_x86_64.cc \
+	optimizing/licm.cc \
 	optimizing/locations.cc \
 	optimizing/nodes.cc \
 	optimizing/optimization.cc \
@@ -241,6 +242,9 @@
   else
     LOCAL_SHARED_LIBRARIES += libvixl
   endif
+
+  LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
   ifeq ($$(art_target_or_host),target)
     # For atrace.
     LOCAL_SHARED_LIBRARIES += libcutils
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 05414b3..8718191 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -116,13 +116,14 @@
   BasicBlock* bb = *p_bb;
   if (mir != NULL) {
     mir = mir->next;
-    if (mir == NULL) {
+    while (mir == NULL) {
       bb = GetBasicBlock(bb->fall_through);
       if ((bb == NULL) || Predecessors(bb) != 1) {
-        mir = NULL;
+        // mir is null and we cannot proceed further.
+        break;
       } else {
-      *p_bb = bb;
-      mir = bb->first_mir_insn;
+        *p_bb = bb;
+        mir = bb->first_mir_insn;
       }
     }
   }
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 5538d79..13f9072 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 
 #include <string>
+#include <sstream>
 
 #include "backend_arm.h"
 #include "base/logging.h"
@@ -490,6 +491,24 @@
        buf += *fmt++;
     }
   }
+  // Dump thread offset.
+  std::string fmt_str = GetTargetInstFmt(lir->opcode);
+  if (std::string::npos != fmt_str.find(", [!1C, #!2") && rARM_SELF == lir->operands[1] &&
+      std::string::npos != buf.find(", [")) {
+    int offset = lir->operands[2];
+    if (std::string::npos != fmt_str.find("#!2d")) {
+    } else if (std::string::npos != fmt_str.find("#!2E")) {
+      offset *= 4;
+    } else if (std::string::npos != fmt_str.find("#!2F")) {
+      offset *= 2;
+    } else {
+      LOG(FATAL) << "Should not reach here";
+    }
+    std::ostringstream tmp_stream;
+    Thread::DumpThreadOffset<4>(tmp_stream, offset);
+    buf += "  ; ";
+    buf += tmp_stream.str();
+  }
   return buf;
 }
 
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index d2204f5..6492442 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -400,8 +400,9 @@
 }
 
 static bool Arm64UseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
-  // Emit relative calls anywhere in the image or within a dex file otherwise.
-  return cu->compiler_driver->IsImage() || cu->dex_file == target_method.dex_file;
+  UNUSED(cu, target_method);
+  // Always emit relative calls.
+  return true;
 }
 
 /*
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 34662f2..136be94 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 
 #include <string>
+#include <sstream>
 
 #include "backend_arm64.h"
 #include "base/logging.h"
@@ -522,6 +523,24 @@
        buf += *fmt++;
     }
   }
+  // Dump thread offset.
+  std::string fmt_str = GetTargetInstFmt(lir->opcode);
+  if (std::string::npos != fmt_str.find(", [!1X, #!2") && rxSELF == lir->operands[1] &&
+      std::string::npos != buf.find(", [")) {
+    int offset = lir->operands[2];
+    if (std::string::npos != fmt_str.find("#!2d")) {
+    } else if (std::string::npos != fmt_str.find("#!2D")) {
+      offset *= (IS_WIDE(lir->opcode)) ? 8 : 4;
+    } else if (std::string::npos != fmt_str.find("#!2F")) {
+      offset *= 2;
+    } else {
+      LOG(FATAL) << "Should not reach here";
+    }
+    std::ostringstream tmp_stream;
+    Thread::DumpThreadOffset<8>(tmp_stream, offset);
+    buf += "  ; ";
+    buf += tmp_stream.str();
+  }
   return buf;
 }
 
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 0337096..43ebf55 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -2303,9 +2303,9 @@
       StoreFinalValue(rl_dest, rl_result);
     } else {
       int displacement = SRegOffset(rl_result.s_reg_low);
+      ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
       LIR *l = NewLIR4(extr_opcode, rs_rX86_SP_32.GetReg(), displacement, vector_src.GetReg(),
                        extract_index);
-      AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is_wide /* is_64bit */);
       AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is_wide /* is_64bit */);
     }
   }
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index cc59a2f..7451bd5 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1267,14 +1267,14 @@
   }
   // TODO: support patching on all architectures.
   use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
-  mirror::Class* declaring_class = method->GetDeclaringClass();
-  bool method_code_in_boot = (declaring_class->GetClassLoader() == nullptr);
+  bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
   if (!use_dex_cache) {
     if (!method_code_in_boot) {
       use_dex_cache = true;
     } else {
-      bool has_clinit_trampoline = method->IsStatic() && !declaring_class->IsInitialized();
-      if (has_clinit_trampoline && (declaring_class != referrer_class)) {
+      bool has_clinit_trampoline =
+          method->IsStatic() && !method->GetDeclaringClass()->IsInitialized();
+      if (has_clinit_trampoline && (method->GetDeclaringClass() != referrer_class)) {
         // Ensure we run the clinit trampoline unless we are invoking a static method in the same
         // class.
         use_dex_cache = true;
@@ -1285,15 +1285,7 @@
     *stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot;
   }
   if (!use_dex_cache && force_relocations) {
-    bool is_in_image;
-    if (IsImage()) {
-      is_in_image = IsImageClass(method->GetDeclaringClassDescriptor());
-    } else {
-      is_in_image = instruction_set_ != kX86 && instruction_set_ != kX86_64 &&
-                    Runtime::Current()->GetHeap()->FindSpaceFromObject(declaring_class,
-                                                                       false)->IsImageSpace();
-    }
-    if (!is_in_image) {
+    if (!IsImage() || !IsImageClass(method->GetDeclaringClassDescriptor())) {
       // We can only branch directly to Methods that are resolved in the DexCache.
       // Otherwise we won't invoke the resolution trampoline.
       use_dex_cache = true;
@@ -1302,7 +1294,7 @@
   // The method is defined not within this dex file. We need a dex cache slot within the current
   // dex file or direct pointers.
   bool must_use_direct_pointers = false;
-  if (target_method->dex_file == declaring_class->GetDexCache()->GetDexFile()) {
+  if (target_method->dex_file == method->GetDeclaringClass()->GetDexCache()->GetDexFile()) {
     target_method->dex_method_index = method->GetDexMethodIndex();
   } else {
     if (no_guarantee_of_dex_cache_entry) {
@@ -1315,7 +1307,7 @@
       } else {
         if (force_relocations && !use_dex_cache) {
           target_method->dex_method_index = method->GetDexMethodIndex();
-          target_method->dex_file = declaring_class->GetDexCache()->GetDexFile();
+          target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
         }
         must_use_direct_pointers = true;
       }
@@ -1338,14 +1330,14 @@
       *type = sharp_type;
       *direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method);
       *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
-      target_method->dex_file = declaring_class->GetDexCache()->GetDexFile();
+      target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
       target_method->dex_method_index = method->GetDexMethodIndex();
     } else if (!must_use_direct_pointers) {
       // Set the code and rely on the dex cache for the method.
       *type = sharp_type;
       if (force_relocations) {
         *direct_code = -1;
-        target_method->dex_file = declaring_class->GetDexCache()->GetDexFile();
+        target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
         target_method->dex_method_index = method->GetDexMethodIndex();
       } else {
         *direct_code = compiler_->GetEntryPointOf(method);
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 7516811..3c36ffa 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1103,18 +1103,10 @@
     if (UNLIKELY(target_offset == 0)) {
       mirror::ArtMethod* target = GetTargetMethod(patch);
       DCHECK(target != nullptr);
-      size_t size = GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet());
-      const void* oat_code_offset = target->GetEntryPointFromQuickCompiledCodePtrSize(size);
-      if (oat_code_offset != 0) {
-        DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset));
-        DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset));
-        DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(oat_code_offset));
-        target_offset = PointerToLowMemUInt32(oat_code_offset);
-      } else {
-        target_offset = target->IsNative()
-            ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
-            : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
-      }
+      DCHECK_EQ(target->GetQuickOatCodeOffset(), 0u);
+      target_offset = target->IsNative()
+          ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
+          : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
     }
     return target_offset;
   }
@@ -1146,9 +1138,10 @@
 
   void PatchCodeAddress(std::vector<uint8_t>* code, uint32_t offset, uint32_t target_offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    uint32_t address = writer_->image_writer_ == nullptr ? target_offset :
-        PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
-                              writer_->oat_data_offset_ + target_offset);
+    // NOTE: Direct calls across oat files don't use linker patches.
+    DCHECK(writer_->image_writer_ != nullptr);
+    uint32_t address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
+                                             writer_->oat_data_offset_ + target_offset);
     DCHECK_LE(offset + 4, code->size());
     uint8_t* data = &(*code)[offset];
     data[0] = address & 0xffu;
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index b6aaab5..3dcb08d 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -652,7 +652,7 @@
   graph->AddBlock(block);
   entry->AddSuccessor(block);
   HInstruction* new_array = new (allocator)
-      HNewArray(constant_10, 0, Primitive::kPrimInt);
+      HNewArray(constant_10, 0, Primitive::kPrimInt, kQuickAllocArray);
   block->AddInstruction(new_array);
   block->AddInstruction(new (allocator) HGoto());
 
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 955deaa..c509606 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -190,37 +190,37 @@
 template<typename T>
 void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_pc) {
   int32_t target_offset = instruction.GetTargetOffset();
-  PotentiallyAddSuspendCheck(target_offset, dex_pc);
+  HBasicBlock* branch_target = FindBlockStartingAt(dex_pc + target_offset);
+  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);
   HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
   T* comparison = new (arena_) T(first, second);
   current_block_->AddInstruction(comparison);
   HInstruction* ifinst = new (arena_) HIf(comparison);
   current_block_->AddInstruction(ifinst);
-  HBasicBlock* target = FindBlockStartingAt(dex_pc + target_offset);
-  DCHECK(target != nullptr);
-  current_block_->AddSuccessor(target);
-  target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
-  DCHECK(target != nullptr);
-  current_block_->AddSuccessor(target);
+  current_block_->AddSuccessor(branch_target);
+  current_block_->AddSuccessor(fallthrough_target);
   current_block_ = nullptr;
 }
 
 template<typename T>
 void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) {
   int32_t target_offset = instruction.GetTargetOffset();
-  PotentiallyAddSuspendCheck(target_offset, dex_pc);
+  HBasicBlock* branch_target = FindBlockStartingAt(dex_pc + target_offset);
+  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);
   T* comparison = new (arena_) T(value, GetIntConstant(0));
   current_block_->AddInstruction(comparison);
   HInstruction* ifinst = new (arena_) HIf(comparison);
   current_block_->AddInstruction(ifinst);
-  HBasicBlock* target = FindBlockStartingAt(dex_pc + target_offset);
-  DCHECK(target != nullptr);
-  current_block_->AddSuccessor(target);
-  target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
-  DCHECK(target != nullptr);
-  current_block_->AddSuccessor(target);
+  current_block_->AddSuccessor(branch_target);
+  current_block_->AddSuccessor(fallthrough_target);
   current_block_ = nullptr;
 }
 
@@ -847,7 +847,10 @@
                                         uint32_t* args,
                                         uint32_t register_index) {
   HInstruction* length = GetIntConstant(number_of_vreg_arguments);
-  HInstruction* object = new (arena_) HNewArray(length, dex_pc, type_index);
+  QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+      ? kQuickAllocArrayWithAccessCheck
+      : kQuickAllocArray;
+  HInstruction* object = new (arena_) HNewArray(length, dex_pc, type_index, entrypoint);
   current_block_->AddInstruction(object);
 
   const char* descriptor = dex_file_->StringByTypeIdx(type_index);
@@ -987,6 +990,11 @@
   return true;
 }
 
+bool HGraphBuilder::NeedsAccessCheck(uint32_t type_index) const {
+  return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
+      dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index);
+}
+
 void HGraphBuilder::BuildPackedSwitch(const Instruction& instruction, uint32_t dex_pc) {
   SwitchTable table(instruction, dex_pc, false);
 
@@ -1026,7 +1034,9 @@
                                           bool is_last_case, const SwitchTable& table,
                                           HInstruction* value, int32_t case_value_int,
                                           int32_t target_offset, uint32_t dex_pc) {
-  PotentiallyAddSuspendCheck(target_offset, 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 = GetIntConstant(case_value_int);
@@ -1038,8 +1048,6 @@
   current_block_->AddInstruction(ifinst);
 
   // Case hit: use the target offset to determine where to go.
-  HBasicBlock* case_target = FindBlockStartingAt(dex_pc + target_offset);
-  DCHECK(case_target != nullptr);
   current_block_->AddSuccessor(case_target);
 
   // Case miss: go to the next case (or default fall-through).
@@ -1064,10 +1072,19 @@
   }
 }
 
-void HGraphBuilder::PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_pc) {
+void HGraphBuilder::PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc) {
+  int32_t target_offset = target->GetDexPc() - dex_pc;
   if (target_offset <= 0) {
-    // Unconditionnally add a suspend check to backward branches. We can remove
-    // them after we recognize loops in the graph.
+    // 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));
   }
 }
@@ -1189,9 +1206,9 @@
     case Instruction::GOTO_16:
     case Instruction::GOTO_32: {
       int32_t offset = instruction.GetTargetOffset();
-      PotentiallyAddSuspendCheck(offset, dex_pc);
       HBasicBlock* target = FindBlockStartingAt(offset + dex_pc);
       DCHECK(target != nullptr);
+      PotentiallyAddSuspendCheck(target, dex_pc);
       current_block_->AddInstruction(new (arena_) HGoto());
       current_block_->AddSuccessor(target);
       current_block_ = nullptr;
@@ -1772,16 +1789,24 @@
     }
 
     case Instruction::NEW_INSTANCE: {
-      current_block_->AddInstruction(
-          new (arena_) HNewInstance(dex_pc, instruction.VRegB_21c()));
+      uint16_t type_index = instruction.VRegB_21c();
+      QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+          ? kQuickAllocObjectWithAccessCheck
+          : kQuickAllocObject;
+
+      current_block_->AddInstruction(new (arena_) HNewInstance(dex_pc, type_index, entrypoint));
       UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
       break;
     }
 
     case Instruction::NEW_ARRAY: {
+      uint16_t type_index = instruction.VRegC_22c();
       HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
+      QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+          ? kQuickAllocArrayWithAccessCheck
+          : kQuickAllocArray;
       current_block_->AddInstruction(
-          new (arena_) HNewArray(length, dex_pc, instruction.VRegC_22c()));
+          new (arena_) HNewArray(length, dex_pc, type_index, entrypoint));
       UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
       break;
     }
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index cc5f6a0..8ee27a1 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -106,8 +106,9 @@
   HLocal* GetLocalAt(int register_index) const;
   void UpdateLocal(int register_index, HInstruction* instruction) const;
   HInstruction* LoadLocal(int register_index, Primitive::Type type) const;
-  void PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_pc);
+  void PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc);
   void InitializeParameters(uint16_t number_of_parameters);
+  bool NeedsAccessCheck(uint32_t type_index) const;
 
   template<typename T>
   void Unop_12x(const Instruction& instruction, Primitive::Type type);
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 0a405c4..dc2446d 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -41,60 +41,57 @@
 }
 
 void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
-  const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
-  DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
-  DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
-  Initialize();
-
   DCHECK_EQ(frame_size_, kUninitializedFrameSize);
+
+  Initialize();
   if (!is_leaf) {
     MarkNotLeaf();
   }
-  ComputeFrameSize(GetGraph()->GetNumberOfLocalVRegs()
-                     + GetGraph()->GetTemporariesVRegSlots()
-                     + 1 /* filler */,
-                   0, /* the baseline compiler does not have live registers at slow path */
-                   0, /* the baseline compiler does not have live registers at slow path */
-                   GetGraph()->GetMaximumNumberOfOutVRegs()
-                     + 1 /* current method */);
-  GenerateFrameEntry();
+  InitializeCodeGeneration(GetGraph()->GetNumberOfLocalVRegs()
+                             + GetGraph()->GetTemporariesVRegSlots()
+                             + 1 /* filler */,
+                           0, /* the baseline compiler does not have live registers at slow path */
+                           0, /* the baseline compiler does not have live registers at slow path */
+                           GetGraph()->GetMaximumNumberOfOutVRegs()
+                             + 1 /* current method */,
+                           GetGraph()->GetBlocks());
+  CompileInternal(allocator, /* is_baseline */ true);
+}
 
+void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) {
   HGraphVisitor* location_builder = GetLocationBuilder();
   HGraphVisitor* instruction_visitor = GetInstructionVisitor();
-  for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
-    HBasicBlock* block = blocks.Get(i);
+  DCHECK_EQ(current_block_index_, 0u);
+  GenerateFrameEntry();
+  for (size_t e = block_order_->Size(); current_block_index_ < e; ++current_block_index_) {
+    HBasicBlock* block = block_order_->Get(current_block_index_);
     Bind(block);
     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* current = it.Current();
-      current->Accept(location_builder);
-      InitLocations(current);
+      if (is_baseline) {
+        current->Accept(location_builder);
+        InitLocations(current);
+      }
       current->Accept(instruction_visitor);
     }
   }
-  GenerateSlowPaths();
+
+  // Generate the slow paths.
+  for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) {
+    slow_paths_.Get(i)->EmitNativeCode(this);
+  }
+
+  // Finalize instructions in assember;
   Finalize(allocator);
 }
 
 void CodeGenerator::CompileOptimized(CodeAllocator* allocator) {
-  // The frame size has already been computed during register allocation.
+  // The register allocator already called `InitializeCodeGeneration`,
+  // where the frame size has been computed.
   DCHECK_NE(frame_size_, kUninitializedFrameSize);
-  const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
-  DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
-  DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
+  DCHECK(block_order_ != nullptr);
   Initialize();
-
-  GenerateFrameEntry();
-  HGraphVisitor* instruction_visitor = GetInstructionVisitor();
-  for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
-    HBasicBlock* block = blocks.Get(i);
-    Bind(block);
-    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
-      HInstruction* current = it.Current();
-      current->Accept(instruction_visitor);
-    }
-  }
-  GenerateSlowPaths();
-  Finalize(allocator);
+  CompileInternal(allocator, /* is_baseline */ false);
 }
 
 void CodeGenerator::Finalize(CodeAllocator* allocator) {
@@ -105,12 +102,6 @@
   GetAssembler()->FinalizeInstructions(code);
 }
 
-void CodeGenerator::GenerateSlowPaths() {
-  for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) {
-    slow_paths_.Get(i)->EmitNativeCode(this);
-  }
-}
-
 size_t CodeGenerator::FindFreeEntry(bool* array, size_t length) {
   for (size_t i = 0; i < length; ++i) {
     if (!array[i]) {
@@ -136,10 +127,14 @@
   return -1;
 }
 
-void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots,
-                                     size_t maximum_number_of_live_core_registers,
-                                     size_t maximum_number_of_live_fp_registers,
-                                     size_t number_of_out_slots) {
+void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots,
+                                             size_t maximum_number_of_live_core_registers,
+                                             size_t maximum_number_of_live_fp_registers,
+                                             size_t number_of_out_slots,
+                                             const GrowableArray<HBasicBlock*>& block_order) {
+  block_order_ = &block_order;
+  DCHECK(block_order_->Get(0) == GetGraph()->GetEntryBlock());
+  DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), block_order_->Get(1)));
   ComputeSpillMask();
   first_register_slot_in_slow_path_ = (number_of_out_slots + number_of_spill_slots) * kVRegSize;
 
@@ -326,8 +321,9 @@
 }
 
 bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
-  // We currently iterate over the block in insertion order.
-  return current->GetBlockId() + 1 == next->GetBlockId();
+  DCHECK_EQ(block_order_->Get(current_block_index_), current);
+  return (current_block_index_ < block_order_->Size() - 1)
+      && (block_order_->Get(current_block_index_ + 1) == next);
 }
 
 CodeGenerator* CodeGenerator::Create(HGraph* graph,
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 45f02e5..ab63b91 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -113,10 +113,11 @@
   virtual size_t GetWordSize() const = 0;
   virtual size_t GetFloatingPointSpillSlotSize() const = 0;
   virtual uintptr_t GetAddressOf(HBasicBlock* block) const = 0;
-  void ComputeFrameSize(size_t number_of_spill_slots,
-                        size_t maximum_number_of_live_core_registers,
-                        size_t maximum_number_of_live_fp_registers,
-                        size_t number_of_out_slots);
+  void InitializeCodeGeneration(size_t number_of_spill_slots,
+                                size_t maximum_number_of_live_core_registers,
+                                size_t maximum_number_of_live_fp_registers,
+                                size_t number_of_out_slots,
+                                const GrowableArray<HBasicBlock*>& block_order);
   int32_t GetStackSlot(HLocal* local) const;
   Location GetTemporaryLocation(HTemporary* temp) const;
 
@@ -181,8 +182,6 @@
     slow_paths_.Add(slow_path);
   }
 
-  void GenerateSlowPaths();
-
   void BuildMappingTable(std::vector<uint8_t>* vector, DefaultSrcMap* src_map) const;
   void BuildVMapTable(std::vector<uint8_t>* vector) const;
   void BuildNativeGCMap(
@@ -253,6 +252,8 @@
         compiler_options_(compiler_options),
         pc_infos_(graph->GetArena(), 32),
         slow_paths_(graph->GetArena(), 8),
+        block_order_(nullptr),
+        current_block_index_(0),
         is_leaf_(true),
         stack_map_stream_(graph->GetArena()) {}
 
@@ -312,6 +313,7 @@
  private:
   void InitLocations(HInstruction* instruction);
   size_t GetStackOffsetOfSavedRegister(size_t index);
+  void CompileInternal(CodeAllocator* allocator, bool is_baseline);
 
   HGraph* const graph_;
   const CompilerOptions& compiler_options_;
@@ -319,6 +321,13 @@
   GrowableArray<PcInfo> pc_infos_;
   GrowableArray<SlowPathCode*> slow_paths_;
 
+  // The order to use for code generation.
+  const GrowableArray<HBasicBlock*>* block_order_;
+
+  // The current block index in `block_order_` of the block
+  // we are generating code for.
+  size_t current_block_index_;
+
   bool is_leaf_;
 
   StackMapStream stack_map_stream_;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 7731a10..b0cd7ba 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -2416,8 +2416,9 @@
   InvokeRuntimeCallingConvention calling_convention;
   codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
   __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
-  codegen_->InvokeRuntime(
-      QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
+  codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
+                          instruction,
+                          instruction->GetDexPc());
 }
 
 void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) {
@@ -2434,8 +2435,9 @@
   InvokeRuntimeCallingConvention calling_convention;
   codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2));
   __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
-  codegen_->InvokeRuntime(
-      QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
+  codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
+                          instruction,
+                          instruction->GetDexPc());
 }
 
 void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 78ae55e..3bc23fe 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2112,7 +2112,9 @@
   codegen_->LoadCurrentMethod(current_method);
   __ Mov(type_index, instruction->GetTypeIndex());
   codegen_->InvokeRuntime(
-      QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
+      GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
+      instruction,
+      instruction->GetDexPc());
   CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck,
                        void*, uint32_t, int32_t, mirror::ArtMethod*>();
 }
@@ -2136,7 +2138,9 @@
   codegen_->LoadCurrentMethod(current_method);
   __ Mov(type_index, instruction->GetTypeIndex());
   codegen_->InvokeRuntime(
-      QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
+      GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
+      instruction,
+      instruction->GetDexPc());
   CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, mirror::ArtMethod*>();
 }
 
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 063550b..c840793 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -17,6 +17,7 @@
 #include "code_generator_x86.h"
 
 #include "entrypoints/quick/quick_entrypoints.h"
+#include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "gc/accounting/card_table.h"
 #include "mirror/array-inl.h"
 #include "mirror/art_method.h"
@@ -2555,8 +2556,7 @@
   codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
   __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
 
-  __ fs()->call(
-      Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
+  __ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())));
 
   codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
@@ -2577,8 +2577,7 @@
   codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2));
   __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
 
-  __ fs()->call(
-      Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocArrayWithAccessCheck)));
+  __ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())));
 
   codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 90b7bda..2ff53a0 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2424,8 +2424,8 @@
   codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
   __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
 
-  __ gs()->call(Address::Absolute(
-      QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
+  __ gs()->call(
+      Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true));
 
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2446,8 +2446,8 @@
   codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(2)));
   __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
 
-  __ gs()->call(Address::Absolute(
-      QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocArrayWithAccessCheck), true));
+  __ gs()->call(
+      Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true));
 
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 35c5269..4ebb136 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -153,8 +153,9 @@
         ? use->GetBlock()->GetPhis()
         : use->GetBlock()->GetInstructions();
     if (!list.Contains(use)) {
-      AddError(StringPrintf("User %d of instruction %d is not defined "
+      AddError(StringPrintf("User %s:%d of instruction %d is not defined "
                             "in a basic block of the control-flow graph.",
+                            use->DebugName(),
                             use->GetId(),
                             instruction->GetId()));
     }
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index ef461d9..22a3d12 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -18,6 +18,7 @@
 
 #include "code_generator.h"
 #include "nodes.h"
+#include "optimization.h"
 #include "ssa_liveness_analysis.h"
 
 namespace art {
@@ -216,6 +217,14 @@
         }
       }
       output_ << " (liveness: " << instruction->GetLifetimePosition() << ")";
+    } else if (pass_name_ == kLoopInvariantCodeMotionPassName) {
+      output_ << " ( loop_header:";
+      HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+      if (info == nullptr) {
+        output_ << "null )";
+      } else {
+        output_ << "B" << info->GetHeader()->GetBlockId() << " )";
+      }
     }
   }
 
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index b90d15e..8d6fe04 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -27,10 +27,6 @@
 class DexCompilationUnit;
 class HGraph;
 
-// TODO: Create an analysis/optimization abstraction.
-static const char* kLivenessPassName = "liveness";
-static const char* kRegisterAllocatorPassName = "register";
-
 /**
  * This class outputs the HGraph in the C1visualizer format.
  * Note: Currently only works if the compiler is single threaded.
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 532167c..41e5164 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -35,24 +35,26 @@
 namespace art {
 
 static constexpr int kMaxInlineCodeUnits = 100;
-static constexpr int kMaxInlineNumberOfBlocks = 3;
+static constexpr int kDepthLimit = 5;
 
 void HInliner::Run() {
-  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
-    for (HInstructionIterator instr_it(it.Current()->GetInstructions());
-         !instr_it.Done();
-         instr_it.Advance()) {
-      HInvokeStaticOrDirect* current = instr_it.Current()->AsInvokeStaticOrDirect();
-      if (current != nullptr) {
-        if (!TryInline(current, current->GetDexMethodIndex(), current->GetInvokeType())) {
+  const GrowableArray<HBasicBlock*>& blocks = graph_->GetReversePostOrder();
+  for (size_t i = 0; i < blocks.Size(); ++i) {
+    HBasicBlock* block = blocks.Get(i);
+    for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) {
+      HInstruction* next = instruction->GetNext();
+      HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect();
+      if (call != nullptr) {
+        if (!TryInline(call, call->GetDexMethodIndex(), call->GetInvokeType())) {
           if (kIsDebugBuild) {
             std::string callee_name =
-                PrettyMethod(current->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
+                PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
             bool should_inline = callee_name.find("$inline$") != std::string::npos;
             CHECK(!should_inline) << "Could not inline " << callee_name;
           }
         }
       }
+      instruction = next;
     }
   }
 }
@@ -137,13 +139,6 @@
     return false;
   }
 
-  if (callee_graph->GetBlocks().Size() > kMaxInlineNumberOfBlocks) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
-                   << " has too many blocks to be inlined: "
-                   << callee_graph->GetBlocks().Size();
-    return false;
-  }
-
   if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph,
                                                   compiler_driver_->GetInstructionSet())) {
     VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
@@ -157,36 +152,6 @@
     return false;
   }
 
-  HReversePostOrderIterator it(*callee_graph);
-  it.Advance();  // Past the entry block to avoid seeing the suspend check.
-  for (; !it.Done(); it.Advance()) {
-    HBasicBlock* block = it.Current();
-    if (block->IsLoopHeader()) {
-      VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
-                     << " could not be inlined because it contains a loop";
-      return false;
-    }
-
-    for (HInstructionIterator instr_it(block->GetInstructions());
-         !instr_it.Done();
-         instr_it.Advance()) {
-      HInstruction* current = instr_it.Current();
-      if (current->CanThrow()) {
-        VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
-                       << " could not be inlined because " << current->DebugName()
-                       << " can throw";
-        return false;
-      }
-
-      if (current->NeedsEnvironment()) {
-        VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
-                       << " could not be inlined because " << current->DebugName()
-                       << " needs an environment";
-        return false;
-      }
-    }
-  }
-
   // Run simple optimizations on the graph.
   SsaRedundantPhiElimination redundant_phi(callee_graph);
   SsaDeadPhiElimination dead_phi(callee_graph);
@@ -207,6 +172,46 @@
     optimization->Run();
   }
 
+  if (depth_ + 1 < kDepthLimit) {
+    HInliner inliner(
+        callee_graph, outer_compilation_unit_, compiler_driver_, outer_stats_, depth_ + 1);
+    inliner.Run();
+  }
+
+  HReversePostOrderIterator it(*callee_graph);
+  it.Advance();  // Past the entry block, it does not contain instructions that prevent inlining.
+  for (; !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    if (block->IsLoopHeader()) {
+      VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                     << " could not be inlined because it contains a loop";
+      return false;
+    }
+
+    for (HInstructionIterator instr_it(block->GetInstructions());
+         !instr_it.Done();
+         instr_it.Advance()) {
+      HInstruction* current = instr_it.Current();
+      if (current->IsSuspendCheck()) {
+        continue;
+      }
+
+      if (current->CanThrow()) {
+        VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                       << " could not be inlined because " << current->DebugName()
+                       << " can throw";
+        return false;
+      }
+
+      if (current->NeedsEnvironment()) {
+        VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                       << " could not be inlined because " << current->DebugName()
+                       << " needs an environment";
+        return false;
+      }
+    }
+  }
+
   callee_graph->InlineInto(graph_, invoke_instruction);
 
   // Now that we have inlined the callee, we need to update the next
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 370e33c..07d893e 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -33,11 +33,13 @@
   HInliner(HGraph* outer_graph,
            const DexCompilationUnit& outer_compilation_unit,
            CompilerDriver* compiler_driver,
-           OptimizingCompilerStats* stats)
+           OptimizingCompilerStats* stats,
+           size_t depth = 0)
       : HOptimization(outer_graph, true, "inliner"),
         outer_compilation_unit_(outer_compilation_unit),
         compiler_driver_(compiler_driver),
-        outer_stats_(stats) {}
+        outer_stats_(stats),
+        depth_(depth) {}
 
   void Run() OVERRIDE;
 
@@ -47,6 +49,7 @@
   const DexCompilationUnit& outer_compilation_unit_;
   CompilerDriver* const compiler_driver_;
   OptimizingCompilerStats* const outer_stats_;
+  const size_t depth_;
 
   DISALLOW_COPY_AND_ASSIGN(HInliner);
 };
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
new file mode 100644
index 0000000..10f24d8
--- /dev/null
+++ b/compiler/optimizing/licm.cc
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 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 "licm.h"
+#include "side_effects_analysis.h"
+
+namespace art {
+
+static bool IsPhiOf(HInstruction* instruction, HBasicBlock* block) {
+  return instruction->IsPhi() && instruction->GetBlock() == block;
+}
+
+/**
+ * Returns whether `instruction` has all its inputs and environment defined
+ * before the loop it is in.
+ */
+static bool InputsAreDefinedBeforeLoop(HInstruction* instruction) {
+  DCHECK(instruction->IsInLoop());
+  HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+  for (HInputIterator it(instruction); !it.Done(); it.Advance()) {
+    HLoopInformation* input_loop = it.Current()->GetBlock()->GetLoopInformation();
+    // We only need to check whether the input is defined in the loop. If it is not
+    // it is defined before the loop.
+    if (input_loop != nullptr && input_loop->IsIn(*info)) {
+      return false;
+    }
+  }
+
+  if (instruction->HasEnvironment()) {
+    HEnvironment* environment = instruction->GetEnvironment();
+    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+      HInstruction* input = environment->GetInstructionAt(i);
+      if (input != nullptr) {
+        HLoopInformation* input_loop = input->GetBlock()->GetLoopInformation();
+        if (input_loop != nullptr && input_loop->IsIn(*info)) {
+          // We can move an instruction that takes a loop header phi in the environment:
+          // we will just replace that phi with its first input later in `UpdateLoopPhisIn`.
+          bool is_loop_header_phi = IsPhiOf(input, info->GetHeader());
+          if (!is_loop_header_phi) {
+            return false;
+          }
+        }
+      }
+    }
+  }
+  return true;
+}
+
+/**
+ * If `environment` has a loop header phi, we replace it with its first input.
+ */
+static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) {
+  for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+    HInstruction* input = environment->GetInstructionAt(i);
+    if (input != nullptr && IsPhiOf(input, info->GetHeader())) {
+      HUseListNode<HEnvironment*>* env_use = environment->GetInstructionEnvUseAt(i);
+      input->RemoveEnvironmentUser(env_use);
+      HInstruction* incoming = input->InputAt(0);
+      environment->SetRawEnvAt(i, incoming);
+      incoming->AddEnvUseAt(environment, i);
+    }
+  }
+}
+
+void LICM::Run() {
+  DCHECK(side_effects_.HasRun());
+  // Only used during debug.
+  ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().Size(), false);
+
+  // Post order visit to visit inner loops before outer loops.
+  for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    if (!block->IsLoopHeader()) {
+      // Only visit the loop when we reach the header.
+      continue;
+    }
+
+    HLoopInformation* loop_info = block->GetLoopInformation();
+    SideEffects loop_effects = side_effects_.GetLoopEffects(block);
+    HBasicBlock* pre_header = loop_info->GetPreHeader();
+
+    for (HBlocksInLoopIterator it_loop(*loop_info); !it_loop.Done(); it_loop.Advance()) {
+      HBasicBlock* inner = it_loop.Current();
+      DCHECK(inner->IsInLoop());
+      if (inner->GetLoopInformation() != loop_info) {
+        // Thanks to post order visit, inner loops were already visited.
+        DCHECK(visited.IsBitSet(inner->GetBlockId()));
+        continue;
+      }
+      visited.SetBit(inner->GetBlockId());
+
+      // We can move an instruction that can throw only if it is the first
+      // throwing instruction in the loop. Note that the first potentially
+      // throwing instruction encountered that is not hoisted stops this
+      // optimization. Non-throwing instruction can still be hoisted.
+      bool found_first_non_hoisted_throwing_instruction_in_loop = !inner->IsLoopHeader();
+      for (HInstructionIterator inst_it(inner->GetInstructions());
+           !inst_it.Done();
+           inst_it.Advance()) {
+        HInstruction* instruction = inst_it.Current();
+        if (instruction->CanBeMoved()
+            && (!instruction->CanThrow() || !found_first_non_hoisted_throwing_instruction_in_loop)
+            && !instruction->GetSideEffects().DependsOn(loop_effects)
+            && InputsAreDefinedBeforeLoop(instruction)) {
+          // We need to update the environment if the instruction has a loop header
+          // phi in it.
+          if (instruction->NeedsEnvironment()) {
+            UpdateLoopPhisIn(instruction->GetEnvironment(), loop_info);
+          }
+          instruction->MoveBefore(pre_header->GetLastInstruction());
+        } else if (instruction->CanThrow()) {
+          // If `instruction` can throw, we cannot move further instructions
+          // that can throw as well.
+          found_first_non_hoisted_throwing_instruction_in_loop = true;
+        }
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h
new file mode 100644
index 0000000..4812394
--- /dev/null
+++ b/compiler/optimizing/licm.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_LICM_H_
+#define ART_COMPILER_OPTIMIZING_LICM_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class SideEffectsAnalysis;
+
+class LICM : public HOptimization {
+ public:
+  LICM(HGraph* graph, const SideEffectsAnalysis& side_effects)
+      : HOptimization(graph, true, kLoopInvariantCodeMotionPassName), side_effects_(side_effects) {}
+
+  void Run() OVERRIDE;
+
+ private:
+  const SideEffectsAnalysis& side_effects_;
+
+  DISALLOW_COPY_AND_ASSIGN(LICM);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_LICM_H_
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index fe9ce74..f1868cb 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -292,6 +292,10 @@
   return true;
 }
 
+void HLoopInformation::Add(HBasicBlock* block) {
+  blocks_.SetBit(block->GetBlockId());
+}
+
 void HLoopInformation::PopulateRecursive(HBasicBlock* block) {
   if (blocks_.IsBitSet(block->GetBlockId())) {
     return;
@@ -707,7 +711,7 @@
   return os;
 }
 
-void HInstruction::InsertBefore(HInstruction* cursor) {
+void HInstruction::MoveBefore(HInstruction* cursor) {
   next_->previous_ = previous_;
   if (previous_ != nullptr) {
     previous_->next_ = next_;
@@ -715,6 +719,7 @@
   if (block_->instructions_.first_instruction_ == this) {
     block_->instructions_.first_instruction_ = next_;
   }
+  DCHECK_NE(block_->instructions_.last_instruction_, this);
 
   previous_ = cursor->previous_;
   if (previous_ != nullptr) {
@@ -723,12 +728,127 @@
   next_ = cursor;
   cursor->previous_ = this;
   block_ = cursor->block_;
+
+  if (block_->instructions_.first_instruction_ == cursor) {
+    block_->instructions_.first_instruction_ = this;
+  }
+}
+
+HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) {
+  DCHECK(!cursor->IsControlFlow());
+  DCHECK_NE(instructions_.last_instruction_, cursor);
+  DCHECK_EQ(cursor->GetBlock(), this);
+
+  HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(), GetDexPc());
+  new_block->instructions_.first_instruction_ = cursor->GetNext();
+  new_block->instructions_.last_instruction_ = instructions_.last_instruction_;
+  cursor->next_->previous_ = nullptr;
+  cursor->next_ = nullptr;
+  instructions_.last_instruction_ = cursor;
+
+  new_block->instructions_.SetBlockOfInstructions(new_block);
+  for (size_t i = 0, e = GetSuccessors().Size(); i < e; ++i) {
+    HBasicBlock* successor = GetSuccessors().Get(i);
+    new_block->successors_.Add(successor);
+    successor->predecessors_.Put(successor->GetPredecessorIndexOf(this), new_block);
+  }
+  successors_.Reset();
+
+  for (size_t i = 0, e = GetDominatedBlocks().Size(); i < e; ++i) {
+    HBasicBlock* dominated = GetDominatedBlocks().Get(i);
+    dominated->dominator_ = new_block;
+    new_block->dominated_blocks_.Add(dominated);
+  }
+  dominated_blocks_.Reset();
+  return new_block;
+}
+
+void HInstructionList::SetBlockOfInstructions(HBasicBlock* block) const {
+  for (HInstruction* current = first_instruction_;
+       current != nullptr;
+       current = current->GetNext()) {
+    current->SetBlock(block);
+  }
+}
+
+void HInstructionList::AddAfter(HInstruction* cursor, const HInstructionList& instruction_list) {
+  DCHECK(Contains(cursor));
+  if (!instruction_list.IsEmpty()) {
+    if (cursor == last_instruction_) {
+      last_instruction_ = instruction_list.last_instruction_;
+    } else {
+      cursor->next_->previous_ = instruction_list.last_instruction_;
+    }
+    instruction_list.last_instruction_->next_ = cursor->next_;
+    cursor->next_ = instruction_list.first_instruction_;
+    instruction_list.first_instruction_->previous_ = cursor;
+  }
+}
+
+void HInstructionList::Add(const HInstructionList& instruction_list) {
+  DCHECK(!IsEmpty());
+  AddAfter(last_instruction_, instruction_list);
+}
+
+void HBasicBlock::MergeWith(HBasicBlock* other) {
+  DCHECK(successors_.IsEmpty()) << "Unimplemented block merge scenario";
+  DCHECK(dominated_blocks_.IsEmpty()) << "Unimplemented block merge scenario";
+  DCHECK(other->GetDominator()->IsEntryBlock() && other->GetGraph() != graph_)
+      << "Unimplemented block merge scenario";
+  DCHECK(other->GetPhis().IsEmpty());
+
+  successors_.Reset();
+  dominated_blocks_.Reset();
+  instructions_.Add(other->GetInstructions());
+  other->GetInstructions().SetBlockOfInstructions(this);
+
+  while (!other->GetSuccessors().IsEmpty()) {
+    HBasicBlock* successor = other->GetSuccessors().Get(0);
+    successor->ReplacePredecessor(other, this);
+  }
+
+  for (size_t i = 0, e = other->GetDominatedBlocks().Size(); i < e; ++i) {
+    HBasicBlock* dominated = other->GetDominatedBlocks().Get(i);
+    dominated_blocks_.Add(dominated);
+    dominated->SetDominator(this);
+  }
+  other->dominated_blocks_.Reset();
+  other->dominator_ = nullptr;
+  other->graph_ = nullptr;
+}
+
+void HBasicBlock::ReplaceWith(HBasicBlock* other) {
+  while (!GetPredecessors().IsEmpty()) {
+    HBasicBlock* predecessor = GetPredecessors().Get(0);
+    predecessor->ReplaceSuccessor(this, other);
+  }
+  while (!GetSuccessors().IsEmpty()) {
+    HBasicBlock* successor = GetSuccessors().Get(0);
+    successor->ReplacePredecessor(this, other);
+  }
+  for (size_t i = 0; i < dominated_blocks_.Size(); ++i) {
+    other->AddDominatedBlock(dominated_blocks_.Get(i));
+  }
+  GetDominator()->ReplaceDominatedBlock(this, other);
+  other->SetDominator(GetDominator());
+  dominator_ = nullptr;
+  graph_ = nullptr;
+}
+
+// Create space in `blocks` for adding `number_of_new_blocks` entries
+// starting at location `at`. Blocks after `at` are moved accordingly.
+static void MakeRoomFor(GrowableArray<HBasicBlock*>* blocks,
+                        size_t number_of_new_blocks,
+                        size_t at) {
+  size_t old_size = blocks->Size();
+  size_t new_size = old_size + number_of_new_blocks;
+  blocks->SetSize(new_size);
+  for (size_t i = old_size - 1, j = new_size - 1; i > at; --i, --j) {
+    blocks->Put(j, blocks->Get(i));
+  }
 }
 
 void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
-  // We currently only support graphs with one entry block, one body block, and one exit block.
-  DCHECK_EQ(GetBlocks().Size(), 3u);
-
   // Walk over the entry block and:
   // - Move constants from the entry block to the outer_graph's entry block,
   // - Replace HParameterValue instructions with their real value.
@@ -737,7 +857,7 @@
   for (HInstructionIterator it(entry_block_->GetInstructions()); !it.Done(); it.Advance()) {
     HInstruction* current = it.Current();
     if (current->IsConstant()) {
-      current->InsertBefore(outer_graph->GetEntryBlock()->GetLastInstruction());
+      current->MoveBefore(outer_graph->GetEntryBlock()->GetLastInstruction());
     } else if (current->IsParameterValue()) {
       current->ReplaceWith(invoke->InputAt(parameter_index++));
     } else {
@@ -746,41 +866,122 @@
     }
   }
 
-  // Insert the body's instructions except the last, just after the `invoke`
-  // instruction.
-  HBasicBlock* body = GetBlocks().Get(1);
-  DCHECK(!body->IsExitBlock());
-  HInstruction* last = body->GetLastInstruction();
-  HInstruction* first = body->GetFirstInstruction();
+  if (GetBlocks().Size() == 3) {
+    // Simple case: Put the first block's instruction into `invoke`'s block.
+    HBasicBlock* body = GetBlocks().Get(1);
+    DCHECK(!body->IsExitBlock());
+    HInstruction* last = body->GetLastInstruction();
 
-  if (first != last) {
-    HInstruction* antelast = last->GetPrevious();
+    invoke->GetBlock()->instructions_.AddAfter(invoke, body->GetInstructions());
+    body->GetInstructions().SetBlockOfInstructions(invoke->GetBlock());
 
-    // Update the instruction list of the body to only contain the last
-    // instruction.
-    last->previous_ = nullptr;
-    body->instructions_.first_instruction_ = last;
-    body->instructions_.last_instruction_ = last;
-
-    // Update the instruction list of the `invoke`'s block to now contain the
-    // body's instructions.
-    antelast->next_ = invoke->GetNext();
-    antelast->next_->previous_ = antelast;
-    first->previous_ = invoke;
-    invoke->next_ = first;
-
-    // Update the block pointer of all instructions.
-    for (HInstruction* current = antelast; current != invoke; current = current->GetPrevious()) {
-      current->SetBlock(invoke->GetBlock());
+    // Replace the invoke with the return value of the inlined graph.
+    if (last->IsReturn()) {
+      invoke->ReplaceWith(last->InputAt(0));
+    } else {
+      DCHECK(last->IsReturnVoid());
     }
-  }
 
-  // Replace the invoke with the return value of the inlined graph.
-  if (last->IsReturn()) {
-    invoke->ReplaceWith(last->InputAt(0));
-    body->RemoveInstruction(last);
+    invoke->GetBlock()->RemoveInstruction(last);
   } else {
-    DCHECK(last->IsReturnVoid());
+    // Need to inline multiple blocks. We split `invoke`'s block
+    // into two blocks, merge the first block of the inlined graph into
+    // the first half, and replace the exit block if the inlined graph
+    // with the second half.
+    ArenaAllocator* allocator = outer_graph->GetArena();
+    HBasicBlock* at = invoke->GetBlock();
+    HBasicBlock* to = at->SplitAfter(invoke);
+
+    HBasicBlock* first = entry_block_->GetSuccessors().Get(0);
+    DCHECK(!first->IsInLoop());
+    at->MergeWith(first);
+    exit_block_->ReplaceWith(to);
+
+    // Update all predecessors of the exit block (now the `to` block)
+    // to not `HReturn` but `HGoto` instead. Also collect the return
+    // values if any, and potentially make it a phi if there are multiple
+    // predecessors.
+    HInstruction* return_value = nullptr;
+    for (size_t i = 0, e = to->GetPredecessors().Size(); i < e; ++i) {
+      HBasicBlock* predecessor = to->GetPredecessors().Get(i);
+      HInstruction* last = predecessor->GetLastInstruction();
+      if (!last->IsReturnVoid()) {
+        if (return_value != nullptr) {
+          if (!return_value->IsPhi()) {
+            HPhi* phi = new (allocator) HPhi(
+                allocator, kNoRegNumber, to->GetPredecessors().Size(), invoke->GetType());
+            return_value->AsPhi()->AddInput(return_value);
+            to->AddPhi(phi);
+            return_value = phi;
+          }
+          return_value->AsPhi()->AddInput(last->InputAt(0));
+        } else {
+          return_value = last->InputAt(0);
+        }
+      }
+      predecessor->AddInstruction(new (allocator) HGoto());
+      predecessor->RemoveInstruction(last);
+    }
+
+    if (return_value != nullptr) {
+      invoke->ReplaceWith(return_value);
+    }
+
+    // Update the meta information surrounding blocks:
+    // (1) the graph they are now in,
+    // (2) the reverse post order of that graph,
+    // (3) the potential loop information they are now in.
+
+    // We don't add the entry block, the exit block, and the first block, which
+    // has been merged with `at`.
+    static constexpr int kNumberOfSkippedBlocksInCallee = 3;
+
+    // We add the `to` block.
+    static constexpr int kNumberOfNewBlocksInCaller = 1;
+    size_t blocks_added = (reverse_post_order_.Size() - kNumberOfSkippedBlocksInCallee)
+        + kNumberOfNewBlocksInCaller;
+
+    // Find the location of `at` in the outer graph's reverse post order. The new
+    // blocks will be added after it.
+    size_t index_of_at = 0;
+    while (outer_graph->reverse_post_order_.Get(index_of_at) != at) {
+      index_of_at++;
+    }
+    MakeRoomFor(&outer_graph->reverse_post_order_, blocks_added, index_of_at);
+
+    // Do a reverse post order of the blocks in the callee and do (1), (2),
+    // and (3) to the blocks that apply.
+    HLoopInformation* info = at->GetLoopInformation();
+    for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
+      HBasicBlock* current = it.Current();
+      if (current != exit_block_ && current != entry_block_ && current != first) {
+        DCHECK(!current->IsInLoop());
+        DCHECK(current->GetGraph() == this);
+        current->SetGraph(outer_graph);
+        outer_graph->AddBlock(current);
+        outer_graph->reverse_post_order_.Put(++index_of_at, current);
+        if (info != nullptr) {
+          info->Add(current);
+          current->SetLoopInformation(info);
+        }
+      }
+    }
+
+    // Do (1), (2), and (3) to `to`.
+    to->SetGraph(outer_graph);
+    outer_graph->AddBlock(to);
+    outer_graph->reverse_post_order_.Put(++index_of_at, to);
+    if (info != nullptr) {
+      info->Add(to);
+      to->SetLoopInformation(info);
+      if (info->IsBackEdge(at)) {
+        // Only `at` can become a back edge, as the inlined blocks
+        // are predecessors of `at`.
+        DCHECK_EQ(1u, info->NumberOfBackEdges());
+        info->ClearBackEdges();
+        info->AddBackEdge(to);
+      }
+    }
   }
 
   // Finally remove the invoke from the caller.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 3e4028e..30d869d 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -17,6 +17,7 @@
 #ifndef ART_COMPILER_OPTIMIZING_NODES_H_
 #define ART_COMPILER_OPTIMIZING_NODES_H_
 
+#include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "invoke_type.h"
 #include "locations.h"
 #include "offsets.h"
@@ -72,6 +73,15 @@
   bool FoundBefore(const HInstruction* instruction1,
                    const HInstruction* instruction2) const;
 
+  bool IsEmpty() const { return first_instruction_ == nullptr; }
+  void Clear() { first_instruction_ = last_instruction_ = nullptr; }
+
+  // Update the block of all instructions to be `block`.
+  void SetBlockOfInstructions(HBasicBlock* block) const;
+
+  void AddAfter(HInstruction* cursor, const HInstructionList& instruction_list);
+  void Add(const HInstructionList& instruction_list);
+
  private:
   HInstruction* first_instruction_;
   HInstruction* last_instruction_;
@@ -240,6 +250,10 @@
     return header_;
   }
 
+  void SetHeader(HBasicBlock* block) {
+    header_ = block;
+  }
+
   HSuspendCheck* GetSuspendCheck() const { return suspend_check_; }
   void SetSuspendCheck(HSuspendCheck* check) { suspend_check_ = check; }
   bool HasSuspendCheck() const { return suspend_check_ != nullptr; }
@@ -287,6 +301,8 @@
 
   const ArenaBitVector& GetBlocks() const { return blocks_; }
 
+  void Add(HBasicBlock* block);
+
  private:
   // Internal recursive implementation of `Populate`.
   void PopulateRecursive(HBasicBlock* block);
@@ -350,6 +366,7 @@
   }
 
   HGraph* GetGraph() const { return graph_; }
+  void SetGraph(HGraph* graph) { graph_ = graph; }
 
   int GetBlockId() const { return block_id_; }
   void SetBlockId(int id) { block_id_ = id; }
@@ -357,6 +374,16 @@
   HBasicBlock* GetDominator() const { return dominator_; }
   void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; }
   void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.Add(block); }
+  void ReplaceDominatedBlock(HBasicBlock* existing, HBasicBlock* new_block) {
+    for (size_t i = 0, e = dominated_blocks_.Size(); i < e; ++i) {
+      if (dominated_blocks_.Get(i) == existing) {
+        dominated_blocks_.Put(i, new_block);
+        return;
+      }
+    }
+    LOG(FATAL) << "Unreachable";
+    UNREACHABLE();
+  }
 
   int NumberOfBackEdges() const {
     return loop_information_ == nullptr
@@ -383,10 +410,22 @@
     successors_.Put(successor_index, new_block);
   }
 
+  void ReplacePredecessor(HBasicBlock* existing, HBasicBlock* new_block) {
+    size_t predecessor_index = GetPredecessorIndexOf(existing);
+    DCHECK_NE(predecessor_index, static_cast<size_t>(-1));
+    existing->RemoveSuccessor(this);
+    new_block->successors_.Add(this);
+    predecessors_.Put(predecessor_index, new_block);
+  }
+
   void RemovePredecessor(HBasicBlock* block) {
     predecessors_.Delete(block);
   }
 
+  void RemoveSuccessor(HBasicBlock* block) {
+    successors_.Delete(block);
+  }
+
   void ClearAllPredecessors() {
     predecessors_.Reset();
   }
@@ -421,6 +460,26 @@
     return -1;
   }
 
+  // Split the block into two blocks just after `cursor`. Returns the newly
+  // created block. Note that this method just updates raw block information,
+  // like predecessors, successors, dominators, and instruction list. It does not
+  // update the graph, reverse post order, loop information, nor make sure the
+  // blocks are consistent (for example ending with a control flow instruction).
+  HBasicBlock* SplitAfter(HInstruction* cursor);
+
+  // Merge `other` at the end of `this`. Successors and dominated blocks of
+  // `other` are changed to be successors and dominated blocks of `this`. Note
+  // that this method does not update the graph, reverse post order, loop
+  // information, nor make sure the blocks are consistent (for example ending
+  // with a control flow instruction).
+  void MergeWith(HBasicBlock* other);
+
+  // Replace `this` with `other`. Predecessors, successors, and dominated blocks
+  // of `this` are moved to `other`.
+  // Note that this method does not update the graph, reverse post order, loop
+  // information, nor make sure the blocks are consistent (for example ending
+  void ReplaceWith(HBasicBlock* other);
+
   void AddInstruction(HInstruction* instruction);
   void RemoveInstruction(HInstruction* instruction);
   void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
@@ -445,8 +504,9 @@
     return loop_information_;
   }
 
-  // Set the loop_information_ on this block. This method overrides the current
+  // Set the loop_information_ on this block. Overrides the current
   // loop_information if it is an outer loop of the passed loop information.
+  // Note that this method is called while creating the loop information.
   void SetInLoop(HLoopInformation* info) {
     if (IsLoopHeader()) {
       // Nothing to do. This just means `info` is an outer loop.
@@ -464,6 +524,11 @@
     }
   }
 
+  // Raw update of the loop information.
+  void SetLoopInformation(HLoopInformation* info) {
+    loop_information_ = info;
+  }
+
   bool IsInLoop() const { return loop_information_ != nullptr; }
 
   // Returns wheter this block dominates the blocked passed as parameter.
@@ -481,7 +546,7 @@
   void SetIsCatchBlock() { is_catch_block_ = true; }
 
  private:
-  HGraph* const graph_;
+  HGraph* graph_;
   GrowableArray<HBasicBlock*> predecessors_;
   GrowableArray<HBasicBlock*> successors_;
   HInstructionList instructions_;
@@ -899,8 +964,8 @@
   void ReplaceWith(HInstruction* instruction);
   void ReplaceInput(HInstruction* replacement, size_t index);
 
-  // Insert `this` instruction in `cursor`'s graph, just before `cursor`.
-  void InsertBefore(HInstruction* cursor);
+  // Move `this` instruction before `cursor`.
+  void MoveBefore(HInstruction* cursor);
 
 #define INSTRUCTION_TYPE_CHECK(type, super)                                    \
   bool Is##type() const { return (As##type() != nullptr); }                    \
@@ -1794,10 +1859,11 @@
 
 class HNewInstance : public HExpression<0> {
  public:
-  HNewInstance(uint32_t dex_pc, uint16_t type_index)
+  HNewInstance(uint32_t dex_pc, uint16_t type_index, QuickEntrypointEnum entrypoint)
       : HExpression(Primitive::kPrimNot, SideEffects::None()),
         dex_pc_(dex_pc),
-        type_index_(type_index) {}
+        type_index_(type_index),
+        entrypoint_(entrypoint) {}
 
   uint32_t GetDexPc() const { return dex_pc_; }
   uint16_t GetTypeIndex() const { return type_index_; }
@@ -1812,11 +1878,14 @@
 
   bool CanBeNull() const OVERRIDE { return false; }
 
+  QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
+
   DECLARE_INSTRUCTION(NewInstance);
 
  private:
   const uint32_t dex_pc_;
   const uint16_t type_index_;
+  const QuickEntrypointEnum entrypoint_;
 
   DISALLOW_COPY_AND_ASSIGN(HNewInstance);
 };
@@ -1837,10 +1906,14 @@
 
 class HNewArray : public HExpression<1> {
  public:
-  HNewArray(HInstruction* length, uint32_t dex_pc, uint16_t type_index)
+  HNewArray(HInstruction* length,
+            uint32_t dex_pc,
+            uint16_t type_index,
+            QuickEntrypointEnum entrypoint)
       : HExpression(Primitive::kPrimNot, SideEffects::None()),
         dex_pc_(dex_pc),
-        type_index_(type_index) {
+        type_index_(type_index),
+        entrypoint_(entrypoint) {
     SetRawInputAt(0, length);
   }
 
@@ -1852,11 +1925,14 @@
 
   bool CanBeNull() const OVERRIDE { return false; }
 
+  QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
+
   DECLARE_INSTRUCTION(NewArray);
 
  private:
   const uint32_t dex_pc_;
   const uint16_t type_index_;
+  const QuickEntrypointEnum entrypoint_;
 
   DISALLOW_COPY_AND_ASSIGN(HNewArray);
 };
@@ -2168,6 +2244,8 @@
   DISALLOW_COPY_AND_ASSIGN(HTypeConversion);
 };
 
+static constexpr uint32_t kNoRegNumber = -1;
+
 class HPhi : public HInstruction {
  public:
   HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
@@ -2562,6 +2640,12 @@
     return MustGenerateClinitCheck() || !is_referrers_class_;
   }
 
+  bool CanThrow() const OVERRIDE {
+    // May call runtime and and therefore can throw.
+    // TODO: finer grain decision.
+    return !is_referrers_class_;
+  }
+
   DECLARE_INSTRUCTION(LoadClass);
 
  private:
@@ -2726,12 +2810,14 @@
 
   bool NeedsEnvironment() const OVERRIDE { return true; }
 
+  bool CanThrow() const OVERRIDE { return true; }
+
   uint32_t GetDexPc() const { return dex_pc_; }
 
   DECLARE_INSTRUCTION(Throw);
 
  private:
-  uint32_t dex_pc_;
+  const uint32_t dex_pc_;
 
   DISALLOW_COPY_AND_ASSIGN(HThrow);
 };
@@ -3063,6 +3149,39 @@
   DISALLOW_COPY_AND_ASSIGN(HPostOrderIterator);
 };
 
+// Iterator over the blocks that art part of the loop. Includes blocks part
+// of an inner loop. The order in which the blocks are iterated is on their
+// block id.
+class HBlocksInLoopIterator : public ValueObject {
+ public:
+  explicit HBlocksInLoopIterator(const HLoopInformation& info)
+      : blocks_in_loop_(info.GetBlocks()),
+        blocks_(info.GetHeader()->GetGraph()->GetBlocks()),
+        index_(0) {
+    if (!blocks_in_loop_.IsBitSet(index_)) {
+      Advance();
+    }
+  }
+
+  bool Done() const { return index_ == blocks_.Size(); }
+  HBasicBlock* Current() const { return blocks_.Get(index_); }
+  void Advance() {
+    ++index_;
+    for (size_t e = blocks_.Size(); index_ < e; ++index_) {
+      if (blocks_in_loop_.IsBitSet(index_)) {
+        break;
+      }
+    }
+  }
+
+ private:
+  const BitVector& blocks_in_loop_;
+  const GrowableArray<HBasicBlock*>& blocks_;
+  size_t index_;
+
+  DISALLOW_COPY_AND_ASSIGN(HBlocksInLoopIterator);
+};
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index e36ef19..9315d89 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -21,6 +21,10 @@
 
 namespace art {
 
+static const char* kLivenessPassName = "liveness";
+static const char* kRegisterAllocatorPassName = "register";
+static const char* kLoopInvariantCodeMotionPassName = "licm";
+
 /**
  * Abstraction to implement an optimization pass.
  */
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 705345b..50d7924 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -34,6 +34,7 @@
 #include "inliner.h"
 #include "instruction_simplifier.h"
 #include "intrinsics.h"
+#include "licm.h"
 #include "jni/quick/jni_compiler.h"
 #include "mirror/art_method-inl.h"
 #include "nodes.h"
@@ -225,6 +226,7 @@
   HConstantFolding fold2(graph);
   SideEffectsAnalysis side_effects(graph);
   GVNOptimization gvn(graph, side_effects);
+  LICM licm(graph, side_effects);
   BoundsCheckElimination bce(graph);
   ReferenceTypePropagation type_propagation(graph);
   InstructionSimplifier simplify2(graph, "instruction_simplifier_after_types");
@@ -242,6 +244,7 @@
     &fold2,
     &side_effects,
     &gvn,
+    &licm,
     &bce,
     &type_propagation,
     &simplify2
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index da6b294..a231a72 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -100,17 +100,16 @@
 TEST(PrettyPrinterTest, CFG3) {
   const char* expected =
     "BasicBlock 0, succ: 1\n"
-    "  5: SuspendCheck\n"
-    "  6: Goto 1\n"
+    "  4: SuspendCheck\n"
+    "  5: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 3\n"
     "  0: Goto 3\n"
     "BasicBlock 2, pred: 3, succ: 4\n"
     "  1: ReturnVoid\n"
     "BasicBlock 3, pred: 1, succ: 2\n"
-    "  2: SuspendCheck\n"
-    "  3: Goto 2\n"
+    "  2: Goto 2\n"
     "BasicBlock 4, pred: 2\n"
-    "  4: Exit\n";
+    "  3: Exit\n";
 
   const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
     Instruction::GOTO | 0x200,
@@ -160,15 +159,14 @@
 TEST(PrettyPrinterTest, CFG5) {
   const char* expected =
     "BasicBlock 0, succ: 1\n"
-    "  4: SuspendCheck\n"
-    "  5: Goto 1\n"
+    "  3: SuspendCheck\n"
+    "  4: Goto 1\n"
     "BasicBlock 1, pred: 0, 2, succ: 3\n"
     "  0: ReturnVoid\n"
     "BasicBlock 2, succ: 1\n"
-    "  1: SuspendCheck\n"
-    "  2: Goto 1\n"
+    "  1: Goto 1\n"
     "BasicBlock 3, pred: 1\n"
-    "  3: Exit\n";
+    "  2: Exit\n";
 
   const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
     Instruction::RETURN_VOID,
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 6f8f688..0a3f24b 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -1459,9 +1459,11 @@
 }
 
 void RegisterAllocator::Resolve() {
-  codegen_->ComputeFrameSize(
-      spill_slots_.Size(), maximum_number_of_live_core_registers_,
-      maximum_number_of_live_fp_registers_, reserved_out_slots_);
+  codegen_->InitializeCodeGeneration(spill_slots_.Size(),
+                                     maximum_number_of_live_core_registers_,
+                                     maximum_number_of_live_fp_registers_,
+                                     reserved_out_slots_,
+                                     liveness_.GetLinearOrder());
 
   // Adjust the Out Location of instructions.
   // TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc
index 96e1c8f..ea1ca5a 100644
--- a/compiler/optimizing/side_effects_analysis.cc
+++ b/compiler/optimizing/side_effects_analysis.cc
@@ -19,6 +19,11 @@
 namespace art {
 
 void SideEffectsAnalysis::Run() {
+  // Inlining might have created more blocks, so we need to increase the size
+  // if needed.
+  block_effects_.SetSize(graph_->GetBlocks().Size());
+  loop_effects_.SetSize(graph_->GetBlocks().Size());
+
   if (kIsDebugBuild) {
     for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
       HBasicBlock* block = it.Current();
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 3eccd3f..5383c28 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -25,8 +25,8 @@
 namespace art {
 namespace arm {
 
-bool Thumb2Assembler::ShifterOperandCanHold(Register rd,
-                                            Register rn,
+bool Thumb2Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED,
+                                            Register rn ATTRIBUTE_UNUSED,
                                             Opcode opcode,
                                             uint32_t immediate,
                                             ShifterOperand* shifter_op) {
@@ -37,13 +37,6 @@
   switch (opcode) {
     case ADD:
     case SUB:
-      if (rn == SP) {
-        if (rd == SP) {
-          return immediate < (1 << 9);    // 9 bits allowed.
-        } else {
-          return immediate < (1 << 12);   // 12 bits.
-        }
-      }
       if (immediate < (1 << 12)) {    // Less than (or equal to) 12 bits can always be done.
         return true;
       }
@@ -713,7 +706,7 @@
   }
 
   bool can_contain_high_register = (opcode == MOV)
-      || ((opcode == ADD) && (rn == rd));
+      || ((opcode == ADD) && (rn == rd) && !set_cc);
 
   if (IsHighRegister(rd) || IsHighRegister(rn)) {
     if (!can_contain_high_register) {
@@ -927,41 +920,71 @@
     if (so.IsImmediate()) {
       use_immediate = true;
       immediate = so.GetImmediate();
+    } else {
+      // Adjust rn and rd: only two registers will be emitted.
+      switch (opcode) {
+        case AND:
+        case ORR:
+        case EOR:
+        case RSB:
+        case ADC:
+        case SBC:
+        case BIC: {
+          if (rn == rd) {
+            rn = so.GetRegister();
+          } else {
+            CHECK_EQ(rd, so.GetRegister());
+          }
+          break;
+        }
+        case CMP:
+        case CMN: {
+          CHECK_EQ(rd, 0);
+          rd = rn;
+          rn = so.GetRegister();
+          break;
+        }
+        case TST:
+        case TEQ:
+        case MVN: {
+          CHECK_EQ(rn, 0);
+          rn = so.GetRegister();
+          break;
+        }
+        default:
+          break;
+      }
     }
 
     switch (opcode) {
       case AND: thumb_opcode = 0U /* 0b0000 */; break;
+      case ORR: thumb_opcode = 12U /* 0b1100 */; break;
       case EOR: thumb_opcode = 1U /* 0b0001 */; break;
-      case SUB: break;
       case RSB: thumb_opcode = 9U /* 0b1001 */; break;
-      case ADD: break;
       case ADC: thumb_opcode = 5U /* 0b0101 */; break;
       case SBC: thumb_opcode = 6U /* 0b0110 */; break;
-      case RSC: break;
-      case TST: thumb_opcode = 8U /* 0b1000 */; rn = so.GetRegister(); break;
-      case TEQ: break;
-      case CMP:
+      case BIC: thumb_opcode = 14U /* 0b1110 */; break;
+      case TST: thumb_opcode = 8U /* 0b1000 */; CHECK(!use_immediate); break;
+      case MVN: thumb_opcode = 15U /* 0b1111 */; CHECK(!use_immediate); break;
+      case CMP: {
         if (use_immediate) {
           // T2 encoding.
-           dp_opcode = 0;
-           opcode_shift = 11;
-           thumb_opcode = 5U /* 0b101 */;
-           rd_shift = 8;
-           rn_shift = 8;
+          dp_opcode = 0;
+          opcode_shift = 11;
+          thumb_opcode = 5U /* 0b101 */;
+          rd_shift = 8;
+          rn_shift = 8;
         } else {
           thumb_opcode = 10U /* 0b1010 */;
-          rd = rn;
-          rn = so.GetRegister();
         }
 
         break;
+      }
       case CMN: {
+        CHECK(!use_immediate);
         thumb_opcode = 11U /* 0b1011 */;
-        rd = rn;
-        rn = so.GetRegister();
         break;
       }
-      case ORR: thumb_opcode = 12U /* 0b1100 */; break;
       case MOV:
         dp_opcode = 0;
         if (use_immediate) {
@@ -984,9 +1007,11 @@
           }
         }
         break;
-      case BIC: thumb_opcode = 14U /* 0b1110 */; break;
-      case MVN: thumb_opcode = 15U /* 0b1111 */; rn = so.GetRegister(); break;
+
+      case TEQ:
+      case RSC:
       default:
+        LOG(FATAL) << "Invalid thumb1 opcode " << opcode;
         break;
     }
   }
@@ -1009,7 +1034,7 @@
 // ADD and SUB are complex enough to warrant their own emitter.
 void Thumb2Assembler::Emit16BitAddSub(Condition cond ATTRIBUTE_UNUSED,
                                       Opcode opcode,
-                                      bool set_cc ATTRIBUTE_UNUSED,
+                                      bool set_cc,
                                       Register rn,
                                       Register rd,
                                       const ShifterOperand& so) {
@@ -1031,7 +1056,7 @@
     case ADD:
       if (so.IsRegister()) {
         Register rm = so.GetRegister();
-        if (rn == rd) {
+        if (rn == rd && !set_cc) {
           // Can use T2 encoding (allows 4 bit registers)
           dp_opcode = 1U /* 0b01 */;
           opcode_shift = 10;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 425ccd7..e571e72 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -30,11 +30,15 @@
   }
 
   std::string GetAssemblerParameters() OVERRIDE {
-    return " -mthumb -mfpu=neon";
+    return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
+  }
+
+  const char* GetAssemblyHeader() OVERRIDE {
+    return kThumb2AssemblyHeader;
   }
 
   std::string GetDisassembleParameters() OVERRIDE {
-    return " -D -bbinary -marm --no-show-raw-insn";
+    return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
   }
 
   void SetUpHelpers() OVERRIDE {
@@ -76,6 +80,8 @@
 
  private:
   std::vector<arm::Register*> registers_;
+
+  static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
 };
 
 
@@ -192,4 +198,21 @@
   DriverStr(expected, "strexd");
 }
 
+TEST_F(AssemblerThumb2Test, eor) {
+#define __ GetAssembler()->
+  __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
+  __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
+  __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
+  __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0));
+  __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8));
+
+  const char* expected =
+      "eors r1, r0\n"
+      "eor r1, r0, r1\n"
+      "eor r1, r8, r0\n"
+      "eor r8, r1, r0\n"
+      "eor r1, r0, r8\n";
+  DriverStr(expected, "abs");
+}
+
 }  // namespace art
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index e3a9580..a171e59 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -309,13 +309,13 @@
   // 16 bit variants.
   __ add(R0, R1, ShifterOperand());
   __ sub(R0, R1, ShifterOperand());
-  __ and_(R0, R1, ShifterOperand());
-  __ orr(R0, R1, ShifterOperand());
-  __ eor(R0, R1, ShifterOperand());
-  __ bic(R0, R1, ShifterOperand());
-  __ adc(R0, R1, ShifterOperand());
-  __ sbc(R0, R1, ShifterOperand());
-  __ rsb(R0, R1, ShifterOperand());
+  __ and_(R0, R0, ShifterOperand(R1));
+  __ orr(R0, R0, ShifterOperand(R1));
+  __ eor(R0, R0, ShifterOperand(R1));
+  __ bic(R0, R0, ShifterOperand(R1));
+  __ adc(R0, R0, ShifterOperand(R1));
+  __ sbc(R0, R0, ShifterOperand(R1));
+  __ rsb(R0, R0, ShifterOperand(R1));
 
   __ tst(R0, ShifterOperand(R1));
   __ teq(R0, ShifterOperand(R1));
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index 239e746..8afd443 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -35,6 +35,7 @@
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := dalvikvm32
 LOCAL_MODULE_STEM_64 := dalvikvm64
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
 include $(BUILD_EXECUTABLE)
 
 # Create symlink for the primary version target.
@@ -67,6 +68,7 @@
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := dalvikvm32
 LOCAL_MODULE_STEM_64 := dalvikvm64
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
 include $(BUILD_HOST_EXECUTABLE)
 
 # Create symlink for the primary version target.
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index 735c850..c9aa8c8 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -84,6 +84,7 @@
 
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+  LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
   # For disassembler_arm64.
   ifeq ($$(art_ndebug_or_debug),debug)
      LOCAL_SHARED_LIBRARIES += libvixld
diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc
index bd3bebf..4ff44b4 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -18,7 +18,7 @@
 
 #include <inttypes.h>
 
-#include <ostream>
+#include <sstream>
 
 #include "base/logging.h"
 #include "base/stringprintf.h"
@@ -27,22 +27,23 @@
 namespace art {
 namespace arm64 {
 
+// This enumeration should mirror the declarations in
+// runtime/arch/arm64/registers_arm64.h. We do not include that file to
+// avoid a dependency on libart.
+enum {
+  TR  = 18,
+  ETR = 21,
+  IP0 = 16,
+  IP1 = 17,
+  FP  = 29,
+  LR  = 30
+};
+
 void CustomDisassembler::AppendRegisterNameToOutput(
     const vixl::Instruction* instr,
     const vixl::CPURegister& reg) {
   USE(instr);
   if (reg.IsRegister()) {
-    // This enumeration should mirror the declarations in
-    // runtime/arch/arm64/registers_arm64.h. We do not include that file to
-    // avoid a dependency on libart.
-    enum {
-      TR  = 18,
-      ETR = 21,
-      IP0 = 16,
-      IP1 = 17,
-      FP  = 29,
-      LR  = 30
-    };
     switch (reg.code()) {
       case IP0: AppendToOutput(reg.Is64Bits() ? "ip0" : "wip0"); return;
       case IP1: AppendToOutput(reg.Is64Bits() ? "ip1" : "wip1"); return;
@@ -66,16 +67,7 @@
     return;
   }
 
-  char* buffer = buffer_;
-  char* buffer_end = buffer_ + buffer_size_;
-
-  // Find the end position in the buffer.
-  while ((*buffer != 0) && (buffer < buffer_end)) {
-    ++buffer;
-  }
-
   void* data_address = instr->LiteralAddress<void*>();
-  ptrdiff_t buf_size_remaining = buffer_end - buffer;
   vixl::Instr op = instr->Mask(vixl::LoadLiteralMask);
 
   switch (op) {
@@ -84,14 +76,14 @@
     case vixl::LDRSW_x_lit: {
       int64_t data = op == vixl::LDR_x_lit ? *reinterpret_cast<int64_t*>(data_address)
                                            : *reinterpret_cast<int32_t*>(data_address);
-      snprintf(buffer, buf_size_remaining, " (0x%" PRIx64 " / %" PRId64 ")", data, data);
+      AppendToOutput(" (0x%" PRIx64 " / %" PRId64 ")", data, data);
       break;
     }
     case vixl::LDR_s_lit:
     case vixl::LDR_d_lit: {
       double data = (op == vixl::LDR_s_lit) ? *reinterpret_cast<float*>(data_address)
                                             : *reinterpret_cast<double*>(data_address);
-      snprintf(buffer, buf_size_remaining, " (%g)", data);
+      AppendToOutput(" (%g)", data);
       break;
     }
     default:
@@ -99,6 +91,17 @@
   }
 }
 
+void CustomDisassembler::VisitLoadStoreUnsignedOffset(const vixl::Instruction* instr) {
+  Disassembler::VisitLoadStoreUnsignedOffset(instr);
+
+  if (instr->Rn() == TR) {
+    int64_t offset = instr->ImmLSUnsigned() << instr->SizeLS();
+    std::ostringstream tmp_stream;
+    Thread::DumpThreadOffset<8>(tmp_stream, static_cast<uint32_t>(offset));
+    AppendToOutput(" (%s)", tmp_stream.str().c_str());
+  }
+}
+
 size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) {
   const vixl::Instruction* instr = reinterpret_cast<const vixl::Instruction*>(begin);
   decoder.Decode(instr);
diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h
index a370b8d..57f11c8 100644
--- a/disassembler/disassembler_arm64.h
+++ b/disassembler/disassembler_arm64.h
@@ -34,11 +34,14 @@
       vixl::Disassembler(), read_literals_(read_literals) {}
 
   // Use register aliases in the disassembly.
-  virtual void AppendRegisterNameToOutput(const vixl::Instruction* instr,
-                                          const vixl::CPURegister& reg) OVERRIDE;
+  void AppendRegisterNameToOutput(const vixl::Instruction* instr,
+                                  const vixl::CPURegister& reg) OVERRIDE;
 
   // Improve the disassembly of literal load instructions.
-  virtual void VisitLoadLiteral(const vixl::Instruction* instr) OVERRIDE;
+  void VisitLoadLiteral(const vixl::Instruction* instr) OVERRIDE;
+
+  // Improve the disassembly of thread offset.
+  void VisitLoadStoreUnsignedOffset(const vixl::Instruction* instr) OVERRIDE;
 
  private:
   // Indicate if the disassembler should read data loaded from literal pools.
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index 1a768c8..203488d 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -119,12 +119,6 @@
   DumpAddrReg(os, rex, reg_num);
 }
 
-static void DumpIndexReg(std::ostream& os, uint8_t rex, uint8_t reg) {
-  bool rex_x = (rex & REX_X) != 0;
-  uint8_t reg_num = rex_x ? (reg + 8) : reg;
-  DumpAddrReg(os, rex, reg_num);
-}
-
 static void DumpOpcodeReg(std::ostream& os, uint8_t rex, uint8_t reg,
                           bool byte_operand, uint8_t size_override) {
   bool rex_b = (rex & REX_B) != 0;
@@ -184,18 +178,30 @@
     uint8_t index = (sib >> 3) & 7;
     uint8_t base = sib & 7;
     address << "[";
+
+    // REX.x is bit 3 of index.
+    if ((rex64 & REX_X) != 0) {
+      index += 8;
+    }
+
+    // Mod = 0 && base = 5 (ebp): no base (ignores REX.b).
+    bool has_base = false;
     if (base != 5 || mod != 0) {
+      has_base = true;
       DumpBaseReg(address, rex64, base);
-      if (index != 4) {
+    }
+
+    // Index = 4 (esp/rsp) is disallowed.
+    if (index != 4) {
+      if (has_base) {
         address << " + ";
       }
-    }
-    if (index != 4) {
-      DumpIndexReg(address, rex64, index);
+      DumpAddrReg(address, rex64, index);
       if (scale != 0) {
         address << StringPrintf(" * %d", 1 << scale);
       }
     }
+
     if (mod == 0) {
       if (base == 5) {
         if (index != 4) {
diff --git a/runtime/Android.mk b/runtime/Android.mk
index a53616a..8e17191 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -300,6 +300,7 @@
   gc/collector/gc_type.h \
   gc/allocator_type.h \
   gc/collector_type.h \
+  gc/space/region_space.h \
   gc/space/space.h \
   gc/heap.h \
   instrumentation.h \
@@ -481,6 +482,8 @@
     LOCAL_MODULE_TARGET_ARCH := $$(ART_TARGET_SUPPORTED_ARCH)
   endif
 
+  LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
   ifeq ($$(art_target_or_host),target)
     ifneq ($$(art_ndebug_or_debug),debug)
       # Leave the symbols in the shared library so that stack unwinders can
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 3430eb5..60e692b 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -756,9 +756,48 @@
 GENERATE_ALL_ALLOC_ENTRYPOINTS
 
 UNIMPLEMENTED art_quick_test_suspend
-UNIMPLEMENTED art_quick_proxy_invoke_handler
+
+    /*
+     * Called by managed code that is attempting to call a method on a proxy class. On entry
+     * r0 holds the proxy method; r1, r2 and r3 may contain arguments.
+     */
+    .extern artQuickProxyInvokeHandler
+ENTRY art_quick_proxy_invoke_handler
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    sd      $a0, 0($sp)            # place proxy method at bottom of frame
+    move    $a2, rSELF             # pass Thread::Current
+    jal     artQuickProxyInvokeHandler  # (Method* proxy method, receiver, Thread*, SP)
+    move    $a3, $sp               # pass $sp
+    ld      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+    daddiu  $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE  # skip a0-a7 and f12-f19
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    bne     $t0, $zero, 1f
+    dmtc1   $v0, $f0               # place return value to FP return value
+    jalr    $zero, $ra
+    dmtc1   $v1, $f1               # place return value to FP return value
+1:
+    DELIVER_PENDING_EXCEPTION
+END art_quick_proxy_invoke_handler
+
 UNIMPLEMENTED art_quick_imt_conflict_trampoline
-UNIMPLEMENTED art_quick_resolution_trampoline
+
+    .extern artQuickResolutionTrampoline
+ENTRY art_quick_resolution_trampoline
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    move    $a2, rSELF             # pass Thread::Current
+    jal     artQuickResolutionTrampoline  # (Method* called, receiver, Thread*, SP)
+    move    $a3, $sp               # pass $sp
+    beq     $v0, $zero, 1f
+    lwu     $a0, 0($sp)            # load resolved method in $a0
+                                   # artQuickResolutionTrampoline puts resolved method in *SP
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    move    $t9, $v0               # code pointer must be in $t9 to generate the global pointer
+    jalr    $zero, $t9             # tail call to method
+    nop
+1:
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    DELIVER_PENDING_EXCEPTION
+END art_quick_resolution_trampoline
 
     .extern artQuickGenericJniTrampoline
     .extern artQuickGenericJniEndTrampoline
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
index 4390180..c3e24a7 100644
--- a/runtime/base/bit_vector.cc
+++ b/runtime/base/bit_vector.cc
@@ -276,6 +276,10 @@
   }
 }
 
+#if defined(__clang__) && defined(__ARM_64BIT_STATE)
+// b/19180814 When POPCOUNT is inlined, boot up failed on arm64 devices.
+__attribute__((optnone))
+#endif
 uint32_t BitVector::NumSetBits(const uint32_t* storage, uint32_t end) {
   uint32_t word_end = WordIndex(end);
   uint32_t partial_word_bits = end & 0x1f;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index d89ad5e..a0e978b 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -4382,6 +4382,10 @@
       LOG(ERROR) << "Invalid class for managed heap object: " << o << " " << c;
       return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
     }
+    if (c->GetClass() == nullptr) {
+      LOG(ERROR) << "Null class of class " << c << " for object " << o;
+      return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+    }
     if (c->IsClassClass()) {
       return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
     }
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 7026b21..734c935 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -819,10 +819,10 @@
   void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
     DCHECK(obj != nullptr);
-    CHECK(collector_->heap_->GetMarkBitmap()->Test(obj)) << obj;
-    CHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) << obj;
+    DCHECK(collector_->heap_->GetMarkBitmap()->Test(obj)) << obj;
+    DCHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) << obj;
     obj->SetReadBarrierPointer(ReadBarrier::WhitePtr());
-    CHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::WhitePtr()) << obj;
+    DCHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::WhitePtr()) << obj;
   }
 
  private:
@@ -964,10 +964,10 @@
   void operator()(mirror::Object* ref) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
     DCHECK(ref != nullptr);
-    CHECK(collector_->region_space_bitmap_->Test(ref)) << ref;
-    CHECK(collector_->region_space_->IsInUnevacFromSpace(ref)) << ref;
+    DCHECK(collector_->region_space_bitmap_->Test(ref)) << ref;
+    DCHECK(collector_->region_space_->IsInUnevacFromSpace(ref)) << ref;
     if (kUseBakerReadBarrier) {
-      CHECK(ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr()) << ref;
+      DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) << ref;
       // Clear the black ptr.
       ref->SetReadBarrierPointer(ReadBarrier::WhitePtr());
     }
@@ -1389,17 +1389,18 @@
 
 mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) {
   DCHECK(from_ref != nullptr);
-  if (region_space_->IsInToSpace(from_ref)) {
+  space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
+  if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
     // It's already marked.
     return from_ref;
   }
   mirror::Object* to_ref;
-  if (region_space_->IsInFromSpace(from_ref)) {
+  if (rtype == space::RegionSpace::RegionType::kRegionTypeFromSpace) {
     to_ref = GetFwdPtr(from_ref);
     DCHECK(to_ref == nullptr || region_space_->IsInToSpace(to_ref) ||
            heap_->non_moving_space_->HasAddress(to_ref))
         << "from_ref=" << from_ref << " to_ref=" << to_ref;
-  } else if (region_space_->IsInUnevacFromSpace(from_ref)) {
+  } else if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
     if (region_space_bitmap_->Test(from_ref)) {
       to_ref = from_ref;
     } else {
@@ -1464,12 +1465,13 @@
   }
   DCHECK(from_ref != nullptr);
   DCHECK(heap_->collector_type_ == kCollectorTypeCC);
-  if (region_space_->IsInToSpace(from_ref)) {
+  space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
+  if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
     // It's already marked.
     return from_ref;
   }
   mirror::Object* to_ref;
-  if (region_space_->IsInFromSpace(from_ref)) {
+  if (rtype == space::RegionSpace::RegionType::kRegionTypeFromSpace) {
     to_ref = GetFwdPtr(from_ref);
     if (kUseBakerReadBarrier) {
       DCHECK(to_ref != ReadBarrier::GrayPtr()) << "from_ref=" << from_ref << " to_ref=" << to_ref;
@@ -1480,7 +1482,7 @@
     }
     DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
         << "from_ref=" << from_ref << " to_ref=" << to_ref;
-  } else if (region_space_->IsInUnevacFromSpace(from_ref)) {
+  } else if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
     // This may or may not succeed, which is ok.
     if (kUseBakerReadBarrier) {
       from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h
index fd00739..a4ed718 100644
--- a/runtime/gc/space/region_space-inl.h
+++ b/runtime/gc/space/region_space-inl.h
@@ -104,7 +104,7 @@
 
 inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, size_t* bytes_allocated,
                                                   size_t* usable_size) {
-  DCHECK_EQ(state_, static_cast<uint8_t>(kRegionToSpace));
+  DCHECK(IsAllocated() && IsInToSpace());
   DCHECK(IsAligned<kAlignment>(num_bytes));
   Atomic<uint8_t*>* atomic_top = reinterpret_cast<Atomic<uint8_t*>*>(&top_);
   uint8_t* old_top;
@@ -132,7 +132,7 @@
   size_t num_bytes = obj->SizeOf();
   if (usable_size != nullptr) {
     if (LIKELY(num_bytes <= kRegionSize)) {
-      DCHECK(RefToRegion(obj)->IsNormal());
+      DCHECK(RefToRegion(obj)->IsAllocated());
       *usable_size = RoundUp(num_bytes, kAlignment);
     } else {
       DCHECK(RefToRegion(obj)->IsLarge());
@@ -142,7 +142,7 @@
   return num_bytes;
 }
 
-template<RegionSpace::SubSpaceType kSubSpaceType>
+template<RegionSpace::RegionType kRegionType>
 uint64_t RegionSpace::GetBytesAllocatedInternal() {
   uint64_t bytes = 0;
   MutexLock mu(Thread::Current(), region_lock_);
@@ -151,33 +151,33 @@
     if (r->IsFree()) {
       continue;
     }
-    switch (kSubSpaceType) {
-      case kAllSpaces:
+    switch (kRegionType) {
+      case RegionType::kRegionTypeAll:
         bytes += r->BytesAllocated();
         break;
-      case kFromSpace:
+      case RegionType::kRegionTypeFromSpace:
         if (r->IsInFromSpace()) {
           bytes += r->BytesAllocated();
         }
         break;
-      case kUnevacFromSpace:
+      case RegionType::kRegionTypeUnevacFromSpace:
         if (r->IsInUnevacFromSpace()) {
           bytes += r->BytesAllocated();
         }
         break;
-      case kToSpace:
+      case RegionType::kRegionTypeToSpace:
         if (r->IsInToSpace()) {
           bytes += r->BytesAllocated();
         }
         break;
       default:
-        LOG(FATAL) << "Unexpected space type : " << static_cast<int>(kSubSpaceType);
+        LOG(FATAL) << "Unexpected space type : " << kRegionType;
     }
   }
   return bytes;
 }
 
-template<RegionSpace::SubSpaceType kSubSpaceType>
+template<RegionSpace::RegionType kRegionType>
 uint64_t RegionSpace::GetObjectsAllocatedInternal() {
   uint64_t bytes = 0;
   MutexLock mu(Thread::Current(), region_lock_);
@@ -186,27 +186,27 @@
     if (r->IsFree()) {
       continue;
     }
-    switch (kSubSpaceType) {
-      case kAllSpaces:
+    switch (kRegionType) {
+      case RegionType::kRegionTypeAll:
         bytes += r->ObjectsAllocated();
         break;
-      case kFromSpace:
+      case RegionType::kRegionTypeFromSpace:
         if (r->IsInFromSpace()) {
           bytes += r->ObjectsAllocated();
         }
         break;
-      case kUnevacFromSpace:
+      case RegionType::kRegionTypeUnevacFromSpace:
         if (r->IsInUnevacFromSpace()) {
           bytes += r->ObjectsAllocated();
         }
         break;
-      case kToSpace:
+      case RegionType::kRegionTypeToSpace:
         if (r->IsInToSpace()) {
           bytes += r->ObjectsAllocated();
         }
         break;
       default:
-        LOG(FATAL) << "Unexpected space type : " << static_cast<int>(kSubSpaceType);
+        LOG(FATAL) << "Unexpected space type : " << kRegionType;
     }
   }
   return bytes;
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index 2ecb79e..2c556d9 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -71,7 +71,7 @@
   }
   full_region_ = Region();
   DCHECK(!full_region_.IsFree());
-  DCHECK(full_region_.IsNormal());
+  DCHECK(full_region_.IsAllocated());
   current_region_ = &full_region_;
   evac_region_ = nullptr;
   size_t ignored;
@@ -115,7 +115,7 @@
 }
 
 inline bool RegionSpace::Region::ShouldBeEvacuated() {
-  DCHECK(state_ == kRegionToSpace || state_ == kRegionLargeToSpace);
+  DCHECK((IsAllocated() || IsLarge()) && IsInToSpace());
   // if the region was allocated after the start of the
   // previous GC or the live ratio is below threshold, evacuate
   // it.
@@ -126,13 +126,13 @@
     bool is_live_percent_valid = live_bytes_ != static_cast<size_t>(-1);
     if (is_live_percent_valid) {
       uint live_percent = GetLivePercent();
-      if (state_ == kRegionToSpace) {
+      if (IsAllocated()) {
         // Side node: live_percent == 0 does not necessarily mean
         // there's no live objects due to rounding (there may be a
         // few).
         result = live_percent < kEvaculateLivePercentThreshold;
       } else {
-        DCHECK(state_ == kRegionLargeToSpace);
+        DCHECK(IsLarge());
         result = live_percent == 0U;
       }
     } else {
@@ -155,11 +155,14 @@
   bool prev_large_evacuated = false;
   for (size_t i = 0; i < num_regions_; ++i) {
     Region* r = &regions_[i];
-    RegionState state = static_cast<RegionState>(r->state_);
+    RegionState state = r->State();
+    RegionType type = r->Type();
     if (!r->IsFree()) {
       DCHECK(r->IsInToSpace());
       if (LIKELY(num_expected_large_tails == 0U)) {
-        DCHECK(state == kRegionToSpace || state == kRegionLargeToSpace);
+        DCHECK((state == RegionState::kRegionStateAllocated ||
+                state == RegionState::kRegionStateLarge) &&
+               type == RegionType::kRegionTypeToSpace);
         bool should_evacuate = force_evacuate_all || r->ShouldBeEvacuated();
         if (should_evacuate) {
           r->SetAsFromSpace();
@@ -168,13 +171,15 @@
           r->SetAsUnevacFromSpace();
           DCHECK(r->IsInUnevacFromSpace());
         }
-        if (UNLIKELY(state == kRegionLargeToSpace)) {
+        if (UNLIKELY(state == RegionState::kRegionStateLarge &&
+                     type == RegionType::kRegionTypeToSpace)) {
           prev_large_evacuated = should_evacuate;
           num_expected_large_tails = RoundUp(r->BytesAllocated(), kRegionSize) / kRegionSize - 1;
           DCHECK_GT(num_expected_large_tails, 0U);
         }
       } else {
-        DCHECK(state == kRegionLargeTailToSpace);
+        DCHECK(state == RegionState::kRegionStateLargeTail &&
+               type == RegionType::kRegionTypeToSpace);
         if (prev_large_evacuated) {
           r->SetAsFromSpace();
           DCHECK(r->IsInFromSpace());
@@ -361,7 +366,7 @@
   if (tlab_start != nullptr) {
     DCHECK(IsAligned<kRegionSize>(tlab_start));
     Region* r = RefToRegionLocked(reinterpret_cast<mirror::Object*>(tlab_start));
-    DCHECK(r->IsNormal());
+    DCHECK(r->IsAllocated());
     DCHECK_EQ(thread->GetThreadLocalBytesAllocated(), kRegionSize);
     r->RecordThreadLocalAllocations(thread->GetThreadLocalObjectsAllocated(),
                                     thread->GetThreadLocalBytesAllocated());
@@ -402,7 +407,8 @@
 void RegionSpace::Region::Dump(std::ostream& os) const {
   os << "Region[" << idx_ << "]=" << reinterpret_cast<void*>(begin_) << "-" << reinterpret_cast<void*>(top_)
      << "-" << reinterpret_cast<void*>(end_)
-     << " state=" << static_cast<uint>(state_) << " objects_allocated=" << objects_allocated_
+     << " state=" << static_cast<uint>(state_) << " type=" << static_cast<uint>(type_)
+     << " objects_allocated=" << objects_allocated_
      << " alloc_time=" << alloc_time_ << " live_bytes=" << live_bytes_
      << " is_newly_allocated=" << is_newly_allocated_ << " is_a_tlab=" << is_a_tlab_ << " thread=" << thread_ << "\n";
 }
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index b4a043f..4160547 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -17,9 +17,10 @@
 #ifndef ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
 #define ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
 
+#include "gc/accounting/read_barrier_table.h"
 #include "object_callbacks.h"
 #include "space.h"
-#include "gc/accounting/read_barrier_table.h"
+#include "thread.h"
 
 namespace art {
 namespace gc {
@@ -94,32 +95,40 @@
   void AssertAllThreadLocalBuffersAreRevoked() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_,
                                                               Locks::thread_list_lock_);
 
-  enum SubSpaceType {
-    kAllSpaces,        // All spaces.
-    kFromSpace,        // From-space. To be evacuated.
-    kUnevacFromSpace,  // Unevacuated from-space. Not to be evacuated.
-    kToSpace,          // To-space.
+  enum class RegionType : uint8_t {
+    kRegionTypeAll,              // All types.
+    kRegionTypeFromSpace,        // From-space. To be evacuated.
+    kRegionTypeUnevacFromSpace,  // Unevacuated from-space. Not to be evacuated.
+    kRegionTypeToSpace,          // To-space.
+    kRegionTypeNone,             // None.
   };
 
-  template<SubSpaceType kSubSpaceType> uint64_t GetBytesAllocatedInternal();
-  template<SubSpaceType kSubSpaceType> uint64_t GetObjectsAllocatedInternal();
+  enum class RegionState : uint8_t {
+    kRegionStateFree,            // Free region.
+    kRegionStateAllocated,       // Allocated region.
+    kRegionStateLarge,           // Large allocated (allocation larger than the region size).
+    kRegionStateLargeTail,       // Large tail (non-first regions of a large allocation).
+  };
+
+  template<RegionType kRegionType> uint64_t GetBytesAllocatedInternal();
+  template<RegionType kRegionType> uint64_t GetObjectsAllocatedInternal();
   uint64_t GetBytesAllocated() {
-    return GetBytesAllocatedInternal<kAllSpaces>();
+    return GetBytesAllocatedInternal<RegionType::kRegionTypeAll>();
   }
   uint64_t GetObjectsAllocated() {
-    return GetObjectsAllocatedInternal<kAllSpaces>();
+    return GetObjectsAllocatedInternal<RegionType::kRegionTypeAll>();
   }
   uint64_t GetBytesAllocatedInFromSpace() {
-    return GetBytesAllocatedInternal<kFromSpace>();
+    return GetBytesAllocatedInternal<RegionType::kRegionTypeFromSpace>();
   }
   uint64_t GetObjectsAllocatedInFromSpace() {
-    return GetObjectsAllocatedInternal<kFromSpace>();
+    return GetObjectsAllocatedInternal<RegionType::kRegionTypeFromSpace>();
   }
   uint64_t GetBytesAllocatedInUnevacFromSpace() {
-    return GetBytesAllocatedInternal<kUnevacFromSpace>();
+    return GetBytesAllocatedInternal<RegionType::kRegionTypeUnevacFromSpace>();
   }
   uint64_t GetObjectsAllocatedInUnevacFromSpace() {
-    return GetObjectsAllocatedInternal<kUnevacFromSpace>();
+    return GetObjectsAllocatedInternal<RegionType::kRegionTypeUnevacFromSpace>();
   }
 
   bool CanMoveObjects() const OVERRIDE {
@@ -181,6 +190,14 @@
     return false;
   }
 
+  RegionType GetRegionType(mirror::Object* ref) {
+    if (HasAddress(ref)) {
+      Region* r = RefToRegionUnlocked(ref);
+      return r->Type();
+    }
+    return RegionType::kRegionTypeNone;
+  }
+
   void SetFromSpace(accounting::ReadBarrierTable* rb_table, bool force_evacuate_all)
       LOCKS_EXCLUDED(region_lock_);
 
@@ -190,7 +207,7 @@
   void ClearFromSpace();
 
   void AddLiveBytes(mirror::Object* ref, size_t alloc_size) {
-    Region* reg = RefToRegion(ref);
+    Region* reg = RefToRegionUnlocked(ref);
     reg->AddLiveBytes(alloc_size);
   }
 
@@ -209,38 +226,36 @@
   template<bool kToSpaceOnly>
   void WalkInternal(ObjectCallback* callback, void* arg) NO_THREAD_SAFETY_ANALYSIS;
 
-  enum RegionState {
-    kRegionFree,                      // Free region.
-    kRegionToSpace,                   // To-space region.
-    kRegionFromSpace,                 // From-space region. To be evacuated.
-    kRegionUnevacFromSpace,           // Unevacuated from-space region. Not to be evacuated.
-    kRegionLargeToSpace,              // Large (allocation larger than the region size) to-space.
-    kRegionLargeFromSpace,            // Large from-space. To be evacuated.
-    kRegionLargeUnevacFromSpace,      // Large unevacuated from-space.
-    kRegionLargeTailToSpace,          // Large tail (non-first regions of a large allocation).
-    kRegionLargeTailFromSpace,        // Large tail from-space.
-    kRegionLargeTailUnevacFromSpace,  // Large tail unevacuated from-space.
-  };
-
   class Region {
    public:
     Region()
         : idx_(static_cast<size_t>(-1)),
-          begin_(nullptr), top_(nullptr), end_(nullptr), state_(kRegionToSpace),
+          begin_(nullptr), top_(nullptr), end_(nullptr),
+          state_(RegionState::kRegionStateAllocated), type_(RegionType::kRegionTypeToSpace),
           objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast<size_t>(-1)),
           is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {}
 
     Region(size_t idx, uint8_t* begin, uint8_t* end)
-        : idx_(idx), begin_(begin), top_(begin), end_(end), state_(kRegionFree),
+        : idx_(idx), begin_(begin), top_(begin), end_(end),
+          state_(RegionState::kRegionStateFree), type_(RegionType::kRegionTypeNone),
           objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast<size_t>(-1)),
           is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {
       DCHECK_LT(begin, end);
       DCHECK_EQ(static_cast<size_t>(end - begin), kRegionSize);
     }
 
+    RegionState State() const {
+      return state_;
+    }
+
+    RegionType Type() const {
+      return type_;
+    }
+
     void Clear() {
       top_ = begin_;
-      state_ = kRegionFree;
+      state_ = RegionState::kRegionStateFree;
+      type_ = RegionType::kRegionTypeNone;
       objects_allocated_ = 0;
       alloc_time_ = 0;
       live_bytes_ = static_cast<size_t>(-1);
@@ -257,8 +272,9 @@
                                         size_t* usable_size);
 
     bool IsFree() const {
-      bool is_free = state_ == kRegionFree;
+      bool is_free = state_ == RegionState::kRegionStateFree;
       if (is_free) {
+        DCHECK(IsInNoSpace());
         DCHECK_EQ(begin_, top_);
         DCHECK_EQ(objects_allocated_, 0U);
       }
@@ -268,19 +284,22 @@
     // Given a free region, declare it non-free (allocated).
     void Unfree(uint32_t alloc_time) {
       DCHECK(IsFree());
-      state_ = kRegionToSpace;
+      state_ = RegionState::kRegionStateAllocated;
+      type_ = RegionType::kRegionTypeToSpace;
       alloc_time_ = alloc_time;
     }
 
     void UnfreeLarge(uint32_t alloc_time) {
       DCHECK(IsFree());
-      state_ = kRegionLargeToSpace;
+      state_ = RegionState::kRegionStateLarge;
+      type_ = RegionType::kRegionTypeToSpace;
       alloc_time_ = alloc_time;
     }
 
     void UnfreeLargeTail(uint32_t alloc_time) {
       DCHECK(IsFree());
-      state_ = kRegionLargeTailToSpace;
+      state_ = RegionState::kRegionStateLargeTail;
+      type_ = RegionType::kRegionTypeToSpace;
       alloc_time_ = alloc_time;
     }
 
@@ -288,25 +307,23 @@
       is_newly_allocated_ = true;
     }
 
-    // Non-large, non-large-tail.
-    bool IsNormal() const {
-      return state_ == kRegionToSpace || state_ == kRegionFromSpace ||
-          state_ == kRegionUnevacFromSpace;
+    // Non-large, non-large-tail allocated.
+    bool IsAllocated() const {
+      return state_ == RegionState::kRegionStateAllocated;
     }
 
+    // Large allocated.
     bool IsLarge() const {
-      bool is_large = state_ == kRegionLargeToSpace || state_ == kRegionLargeFromSpace ||
-          state_ == kRegionLargeUnevacFromSpace;
+      bool is_large = state_ == RegionState::kRegionStateLarge;
       if (is_large) {
         DCHECK_LT(begin_ + 1 * MB, top_);
       }
       return is_large;
     }
 
+    // Large-tail allocated.
     bool IsLargeTail() const {
-      bool is_large_tail = state_ == kRegionLargeTailToSpace ||
-          state_ == kRegionLargeTailFromSpace ||
-          state_ == kRegionLargeTailUnevacFromSpace;
+      bool is_large_tail = state_ == RegionState::kRegionStateLargeTail;
       if (is_large_tail) {
         DCHECK_EQ(begin_, top_);
       }
@@ -318,71 +335,36 @@
     }
 
     bool IsInFromSpace() const {
-      return state_ == kRegionFromSpace || state_ == kRegionLargeFromSpace ||
-          state_ == kRegionLargeTailFromSpace;
+      return type_ == RegionType::kRegionTypeFromSpace;
     }
 
     bool IsInToSpace() const {
-      return state_ == kRegionToSpace || state_ == kRegionLargeToSpace ||
-          state_ == kRegionLargeTailToSpace;
+      return type_ == RegionType::kRegionTypeToSpace;
     }
 
     bool IsInUnevacFromSpace() const {
-      return state_ == kRegionUnevacFromSpace || state_ == kRegionLargeUnevacFromSpace ||
-          state_ == kRegionLargeTailUnevacFromSpace;
+      return type_ == RegionType::kRegionTypeUnevacFromSpace;
+    }
+
+    bool IsInNoSpace() const {
+      return type_ == RegionType::kRegionTypeNone;
     }
 
     void SetAsFromSpace() {
-      switch (state_) {
-        case kRegionToSpace:
-          state_ = kRegionFromSpace;
-          break;
-        case kRegionLargeToSpace:
-          state_ = kRegionLargeFromSpace;
-          break;
-        case kRegionLargeTailToSpace:
-          state_ = kRegionLargeTailFromSpace;
-          break;
-        default:
-          LOG(FATAL) << "Unexpected region state : " << static_cast<uint>(state_)
-                     << " idx=" << idx_;
-      }
+      DCHECK(!IsFree() && IsInToSpace());
+      type_ = RegionType::kRegionTypeFromSpace;
       live_bytes_ = static_cast<size_t>(-1);
     }
 
     void SetAsUnevacFromSpace() {
-      switch (state_) {
-        case kRegionToSpace:
-          state_ = kRegionUnevacFromSpace;
-          break;
-        case kRegionLargeToSpace:
-          state_ = kRegionLargeUnevacFromSpace;
-          break;
-        case kRegionLargeTailToSpace:
-          state_ = kRegionLargeTailUnevacFromSpace;
-          break;
-        default:
-          LOG(FATAL) << "Unexpected region state : " << static_cast<uint>(state_)
-                     << " idx=" << idx_;
-      }
+      DCHECK(!IsFree() && IsInToSpace());
+      type_ = RegionType::kRegionTypeUnevacFromSpace;
       live_bytes_ = 0U;
     }
 
     void SetUnevacFromSpaceAsToSpace() {
-      switch (state_) {
-        case kRegionUnevacFromSpace:
-          state_ = kRegionToSpace;
-          break;
-        case kRegionLargeUnevacFromSpace:
-          state_ = kRegionLargeToSpace;
-          break;
-        case kRegionLargeTailUnevacFromSpace:
-          state_ = kRegionLargeTailToSpace;
-          break;
-        default:
-          LOG(FATAL) << "Unexpected region state : " << static_cast<uint>(state_)
-                     << " idx=" << idx_;
-      }
+      DCHECK(!IsFree() && IsInUnevacFromSpace());
+      type_ = RegionType::kRegionTypeToSpace;
     }
 
     ALWAYS_INLINE bool ShouldBeEvacuated();
@@ -419,7 +401,7 @@
         DCHECK_EQ(begin_, top_);
         return 0;
       } else {
-        DCHECK(IsNormal()) << static_cast<uint>(state_);
+        DCHECK(IsAllocated()) << static_cast<uint>(state_);
         DCHECK_LE(begin_, top_);
         size_t bytes = static_cast<size_t>(top_ - begin_);
         DCHECK_LE(bytes, kRegionSize);
@@ -437,7 +419,7 @@
         DCHECK_EQ(objects_allocated_, 0U);
         return 0;
       } else {
-        DCHECK(IsNormal()) << static_cast<uint>(state_);
+        DCHECK(IsAllocated()) << static_cast<uint>(state_);
         return objects_allocated_;
       }
     }
@@ -465,7 +447,7 @@
     void Dump(std::ostream& os) const;
 
     void RecordThreadLocalAllocations(size_t num_objects, size_t num_bytes) {
-      DCHECK(IsNormal());
+      DCHECK(IsAllocated());
       DCHECK_EQ(objects_allocated_, 0U);
       DCHECK_EQ(top_, end_);
       objects_allocated_ = num_objects;
@@ -479,7 +461,8 @@
     // Can't use Atomic<uint8_t*> as Atomic's copy operator is implicitly deleted.
     uint8_t* top_;                 // The current position of the allocation.
     uint8_t* end_;                 // The end address of the region.
-    uint8_t state_;                // The region state (see RegionState).
+    RegionState state_;            // The region state (see RegionState).
+    RegionType type_;              // The region type (see RegionType).
     uint64_t objects_allocated_;   // The number of objects allocated.
     uint32_t alloc_time_;          // The allocation time of the region.
     size_t live_bytes_;            // The live bytes. Used to compute the live percent.
@@ -534,6 +517,9 @@
   DISALLOW_COPY_AND_ASSIGN(RegionSpace);
 };
 
+std::ostream& operator<<(std::ostream& os, const RegionSpace::RegionState& value);
+std::ostream& operator<<(std::ostream& os, const RegionSpace::RegionType& value);
+
 }  // namespace space
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 4551d5b..96430a0 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -18,10 +18,6 @@
 
 #include <sstream>
 
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
 #include "base/stringpiece.h"
 #include "debugger.h"
 #include "gc/heap.h"
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index b3da134..c3bdcb1 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -40,10 +40,6 @@
 #include "thread.h"
 #include "thread_list.h"
 
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
 #include "entrypoints/quick/quick_entrypoints.h"
 
 namespace art {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 57a849a..3acac3a 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -33,6 +33,8 @@
 #include <vector>
 #include <fcntl.h>
 
+#include "JniConstants.h"
+#include "ScopedLocalRef.h"
 #include "arch/arm/quick_method_frame_info_arm.h"
 #include "arch/arm/registers_arm.h"
 #include "arch/arm64/quick_method_frame_info_arm64.h"
@@ -59,19 +61,19 @@
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
 #include "gc/space/space.h"
+#include "handle_scope-inl.h"
 #include "image.h"
 #include "instrumentation.h"
 #include "intern_table.h"
 #include "jni_internal.h"
+#include "mirror/array.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
-#include "mirror/array.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/stack_trace_element.h"
 #include "mirror/throwable.h"
 #include "monitor.h"
-#include "native_bridge_art_interface.h"
 #include "native/dalvik_system_DexFile.h"
 #include "native/dalvik_system_VMDebug.h"
 #include "native/dalvik_system_VMRuntime.h"
@@ -80,48 +82,41 @@
 #include "native/java_lang_Class.h"
 #include "native/java_lang_DexCache.h"
 #include "native/java_lang_Object.h"
-#include "native/java_lang_ref_FinalizerReference.h"
-#include "native/java_lang_reflect_Array.h"
-#include "native/java_lang_reflect_Constructor.h"
-#include "native/java_lang_reflect_Field.h"
-#include "native/java_lang_reflect_Method.h"
-#include "native/java_lang_reflect_Proxy.h"
-#include "native/java_lang_ref_Reference.h"
 #include "native/java_lang_Runtime.h"
 #include "native/java_lang_String.h"
 #include "native/java_lang_System.h"
 #include "native/java_lang_Thread.h"
 #include "native/java_lang_Throwable.h"
 #include "native/java_lang_VMClassLoader.h"
+#include "native/java_lang_ref_FinalizerReference.h"
+#include "native/java_lang_ref_Reference.h"
+#include "native/java_lang_reflect_Array.h"
+#include "native/java_lang_reflect_Constructor.h"
+#include "native/java_lang_reflect_Field.h"
+#include "native/java_lang_reflect_Method.h"
+#include "native/java_lang_reflect_Proxy.h"
 #include "native/java_util_concurrent_atomic_AtomicLong.h"
 #include "native/org_apache_harmony_dalvik_ddmc_DdmServer.h"
 #include "native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.h"
 #include "native/sun_misc_Unsafe.h"
-#include "parsed_options.h"
+#include "native_bridge_art_interface.h"
 #include "oat_file.h"
 #include "os.h"
+#include "parsed_options.h"
+#include "profiler.h"
 #include "quick/quick_method_frame_info.h"
 #include "reflection.h"
-#include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
 #include "sigchain.h"
 #include "signal_catcher.h"
 #include "signal_set.h"
-#include "handle_scope-inl.h"
 #include "thread.h"
 #include "thread_list.h"
 #include "trace.h"
 #include "transaction.h"
-#include "profiler.h"
 #include "verifier/method_verifier.h"
 #include "well_known_classes.h"
 
-#include "JniConstants.h"  // Last to avoid LOG redefinition in ics-mr1-plus-art.
-
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
 namespace art {
 
 // If a signal isn't handled properly, enable a handler that attempts to dump the Java stack.
diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk
index e0f1769..e1aae11 100644
--- a/sigchainlib/Android.mk
+++ b/sigchainlib/Android.mk
@@ -28,6 +28,7 @@
 LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
 $(eval $(call set-target-local-clang-vars))
 include $(BUILD_SHARED_LIBRARY)
 
@@ -56,6 +57,7 @@
 LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
 LOCAL_LDLIBS = -ldl
 LOCAL_MULTILIB := both
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
diff --git a/test/074-gc-thrash/src/Main.java b/test/074-gc-thrash/src/Main.java
index 32fbf2d..238e73a 100644
--- a/test/074-gc-thrash/src/Main.java
+++ b/test/074-gc-thrash/src/Main.java
@@ -292,8 +292,8 @@
                     break;
             }
 
-            strong[depth] = funStr;
             weak[depth] = new WeakReference(funStr);
+            strong[depth] = funStr;
             if (depth+1 < MAX_DEPTH)
                 dive(depth+1, iteration+1);
             else
diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java
index 7f24b1b..4d781c3 100644
--- a/test/099-vmdebug/src/Main.java
+++ b/test/099-vmdebug/src/Main.java
@@ -28,14 +28,22 @@
         testMethodTracing();
     }
 
-    private static void testMethodTracing() throws Exception {
-        File tempFile;
+    private static File createTempFile() throws Exception {
         try {
-            tempFile = File.createTempFile("test", ".trace");
+            return  File.createTempFile("test", ".trace");
         } catch (IOException e) {
-            System.setProperty("java.io.tmpdir", "/sdcard");
-            tempFile = File.createTempFile("test", ".trace");
+            System.setProperty("java.io.tmpdir", "/data/local/tmp");
+            try {
+                return File.createTempFile("test", ".trace");
+            } catch (IOException e2) {
+                System.setProperty("java.io.tmpdir", "/sdcard");
+                return File.createTempFile("test", ".trace");
+            }
         }
+    }
+
+    private static void testMethodTracing() throws Exception {
+        File tempFile = createTempFile();
         tempFile.deleteOnExit();
         String tempFileName = tempFile.getPath();
 
diff --git a/test/445-checker-licm/expected.txt b/test/445-checker-licm/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/445-checker-licm/expected.txt
diff --git a/test/445-checker-licm/info.txt b/test/445-checker-licm/info.txt
new file mode 100644
index 0000000..e09d958
--- /dev/null
+++ b/test/445-checker-licm/info.txt
@@ -0,0 +1 @@
+Checker test for testing loop invariant code motion.
diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java
new file mode 100644
index 0000000..91ac2ed
--- /dev/null
+++ b/test/445-checker-licm/src/Main.java
@@ -0,0 +1,123 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+  // CHECK-START: int Main.div() licm (before)
+  // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.div() licm (after)
+  // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.div() licm (after)
+  // CHECK-DAG: Div ( loop_header:null )
+
+  public static int div() {
+    int result = 0;
+    for (int i = 0; i < 10; ++i) {
+      result += staticField / 42;
+    }
+    return result;
+  }
+
+  // CHECK-START: int Main.innerDiv() licm (before)
+  // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.innerDiv() licm (after)
+  // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.innerDiv() licm (after)
+  // CHECK-DAG: Div ( loop_header:null )
+
+  public static int innerDiv() {
+    int result = 0;
+    for (int i = 0; i < 10; ++i) {
+      for (int j = 0; j < 10; ++j) {
+        result += staticField / 42;
+      }
+    }
+    return result;
+  }
+
+  // CHECK-START: int Main.innerDiv2() licm (before)
+  // CHECK-DAG: Mul ( loop_header:{{B4}} )
+
+  // CHECK-START: int Main.innerDiv2() licm (after)
+  // CHECK-DAG: Mul ( loop_header:{{B2}} )
+
+  public static int innerDiv2() {
+    int result = 0;
+    for (int i = 0; i < 10; ++i) {
+      for (int j = 0; j < 10; ++j) {
+        // The operation has been hoisted out of the inner loop.
+        // Note that we depend on the compiler's block numbering to
+        // check if it has been moved.
+        result += staticField * i;
+      }
+    }
+    return result;
+  }
+
+  // CHECK-START: int Main.innerDiv3(int, int) licm (before)
+  // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.innerDiv3(int, int) licm (after)
+  // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+  public static int innerDiv3(int a, int b) {
+    int result = 0;
+    while (b < 5) {
+      // a might be null, so we can't hoist the operation.
+      result += staticField / a;
+      b++;
+    }
+    return result;
+  }
+
+  // CHECK-START: int Main.arrayLength(int[]) licm (before)
+  // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:{{B\d+}} )
+  // CHECK-DAG:                    ArrayLength [ [[NullCheck]] ] ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.arrayLength(int[]) licm (after)
+  // CHECK-NOT:                    NullCheck ( loop_header:{{B\d+}} )
+  // CHECK-NOT:                    ArrayLength ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.arrayLength(int[]) licm (after)
+  // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:null )
+  // CHECK-DAG:                    ArrayLength [ [[NullCheck]] ] ( loop_header:null )
+
+  public static int arrayLength(int[] array) {
+    int result = 0;
+    for (int i = 0; i < array.length; ++i) {
+      result += array[i];
+    }
+    return result;
+  }
+
+  public static int staticField = 42;
+
+  public static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new Error("Expected " + expected + ", got " + actual);
+    }
+  }
+
+  public static void main(String[] args) {
+    assertEquals(10, div());
+    assertEquals(100, innerDiv());
+    assertEquals(12, arrayLength(new int[] { 4, 8 }));
+  }
+}
diff --git a/test/446-checker-inliner2/expected.txt b/test/446-checker-inliner2/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/446-checker-inliner2/expected.txt
diff --git a/test/446-checker-inliner2/info.txt b/test/446-checker-inliner2/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/446-checker-inliner2/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/test/446-checker-inliner2/src/Main.java b/test/446-checker-inliner2/src/Main.java
new file mode 100644
index 0000000..ecf071e
--- /dev/null
+++ b/test/446-checker-inliner2/src/Main.java
@@ -0,0 +1,72 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+  // CHECK-START: int Main.inlineInstanceCall(Main) inliner (before)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
+  // CHECK-DAG:     [[Field:i\d+]]   InstanceFieldGet
+  // CHECK-DAG:                      Return [ [[Field]] ]
+
+  public static int inlineInstanceCall(Main m) {
+    return m.foo();
+  }
+
+  private int foo() {
+    return field;
+  }
+
+  int field = 42;
+
+  // CHECK-START: int Main.inlineNestedCall() inliner (before)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: int Main.inlineNestedCall() inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  // CHECK-START: int Main.inlineNestedCall() inliner (after)
+  // CHECK-DAG:     [[Const38:i\d+]] IntConstant 38
+  // CHECK-DAG:                      Return [ [[Const38]] ]
+
+  public static int inlineNestedCall() {
+    return nestedCall();
+  }
+
+  public static int nestedCall() {
+    return bar();
+  }
+
+  public static int bar() {
+    return 38;
+  }
+
+  public static void main(String[] args) {
+    if (inlineInstanceCall(new Main()) != 42) {
+      throw new Error("Expected 42");
+    }
+
+    if (inlineNestedCall() != 38) {
+      throw new Error("Expected 38");
+    }
+  }
+}
diff --git a/test/447-checker-inliner3/expected.txt b/test/447-checker-inliner3/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/447-checker-inliner3/expected.txt
diff --git a/test/447-checker-inliner3/info.txt b/test/447-checker-inliner3/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/447-checker-inliner3/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/test/447-checker-inliner3/src/Main.java b/test/447-checker-inliner3/src/Main.java
new file mode 100644
index 0000000..db4b236
--- /dev/null
+++ b/test/447-checker-inliner3/src/Main.java
@@ -0,0 +1,77 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+  // CHECK-START: int Main.inlineIfThenElse() inliner (before)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: int Main.inlineIfThenElse() inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  public static int inlineIfThenElse() {
+    return foo(true);
+  }
+
+  private static int foo(boolean value) {
+    if (value) {
+      return 1;
+    } else {
+      return 0;
+    }
+  }
+
+  // CHECK-START: int Main.inlineInLoop() inliner (before)
+  // CHECK-DAG:     InvokeStaticOrDirect
+
+  // CHECK-START: int Main.inlineInLoop() inliner (after)
+  // CHECK-NOT:     InvokeStaticOrDirect
+
+  public static int inlineInLoop() {
+    int result = 0;
+    for (int i = 0; i < 32; ++i) {
+      result += foo(i % 2 == 0);
+    }
+    return result;
+  }
+
+  // CHECK-START: int Main.inlineInLoopHeader() inliner (before)
+  // CHECK-DAG:     InvokeStaticOrDirect
+
+  // CHECK-START: int Main.inlineInLoopHeader() inliner (after)
+  // CHECK-NOT:     InvokeStaticOrDirect
+
+  public static int inlineInLoopHeader() {
+    int result = 0;
+    for (int i = 0; i < foo(i % 2 == 0); ++i) {
+      result += 42;
+    }
+    return result;
+  }
+
+  public static void main(String[] args) {
+    if (inlineIfThenElse() != 1) {
+      throw new Error("Expected 1");
+    }
+    if (inlineInLoop() != 16) {
+      throw new Error("Expected 16");
+    }
+    if (inlineInLoopHeader() != 42) {
+      throw new Error("Expected 16");
+    }
+  }
+}
diff --git a/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java b/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
index a88d31b..9c41abf 100644
--- a/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
+++ b/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
@@ -21,10 +21,10 @@
 
   public static void throwExceptionDuringDeopt(int i) {
     if (i == 0) {
-      DeoptimizationController.startDeoptomization();
+      DeoptimizationController.startDeoptimization();
       throw new RuntimeException("Test exception");
     } else {
-      DeoptimizationController.stopDeoptomization();
+      DeoptimizationController.stopDeoptimization();
     }
   }
 }
diff --git a/test/802-deoptimization/src/DeoptimizationController.java b/test/802-deoptimization/src/DeoptimizationController.java
index c031c07..c926669 100644
--- a/test/802-deoptimization/src/DeoptimizationController.java
+++ b/test/802-deoptimization/src/DeoptimizationController.java
@@ -22,15 +22,23 @@
  * Controls deoptimization using dalvik.system.VMDebug class.
  */
 public class DeoptimizationController {
-  public static void startDeoptomization() {
+  private static File createTempFile() throws Exception {
     try {
-      File tempFile;
+      return  File.createTempFile("test", ".trace");
+    } catch (IOException e) {
+      System.setProperty("java.io.tmpdir", "/data/local/tmp");
       try {
-        tempFile = File.createTempFile("test", ".trace");
-      } catch (IOException e) {
+        return File.createTempFile("test", ".trace");
+      } catch (IOException e2) {
         System.setProperty("java.io.tmpdir", "/sdcard");
-        tempFile = File.createTempFile("test", ".trace");
+        return File.createTempFile("test", ".trace");
       }
+    }
+  }
+
+  public static void startDeoptimization() {
+    try {
+      File tempFile = createTempFile();
       tempFile.deleteOnExit();
       String tempFileName = tempFile.getPath();
 
@@ -43,7 +51,7 @@
     }
   }
 
-  public static void stopDeoptomization() {
+  public static void stopDeoptimization() {
     try {
       VMDebug.stopMethodTracing();
       if (VMDebug.getMethodTracingMode() != 0) {