Merge "Cleanup thread access in StackVisitor"
diff --git a/Android.mk b/Android.mk
index 8859d3a..b8ba9f2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -33,7 +33,7 @@
 
 # Don't bother with tests unless there is a test-art*, build-art*, or related target.
 art_test_bother := false
-ifneq (,$(filter %tests test-art% valgrind-test-art% build-art%,$(MAKECMDGOALS)))
+ifneq (,$(filter tests test-art% valgrind-test-art% build-art% checkbuild,$(MAKECMDGOALS)))
   art_test_bother := true
 endif
 
@@ -119,6 +119,7 @@
 include $(art_path)/build/Android.common_test.mk
 include $(art_path)/build/Android.gtest.mk
 include $(art_path)/test/Android.run-test.mk
+include $(art_path)/benchmark/Android.mk
 
 # Sync test files to the target, depends upon all things that must be pushed to the target.
 .PHONY: test-art-target-sync
diff --git a/benchmark/Android.mk b/benchmark/Android.mk
new file mode 100644
index 0000000..09aca98
--- /dev/null
+++ b/benchmark/Android.mk
@@ -0,0 +1,78 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include art/build/Android.common_build.mk
+
+LIBARTBENCHMARK_COMMON_SRC_FILES := \
+  jni-perf/perf_jni.cc \
+  scoped-primitive-array/scoped_primitive_array.cc
+
+# $(1): target or host
+define build-libartbenchmark
+  ifneq ($(1),target)
+    ifneq ($(1),host)
+      $$(error expected target or host for argument 1, received $(1))
+    endif
+  endif
+
+  art_target_or_host := $(1)
+
+  include $(CLEAR_VARS)
+  LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+  LOCAL_MODULE := libartbenchmark
+  ifeq ($$(art_target_or_host),target)
+    LOCAL_MODULE_TAGS := tests
+  endif
+  LOCAL_SRC_FILES := $(LIBARTBENCHMARK_COMMON_SRC_FILES)
+  LOCAL_SHARED_LIBRARIES += libart libbacktrace libnativehelper
+  LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
+  LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
+  LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+  ifeq ($$(art_target_or_host),target)
+    $(call set-target-local-clang-vars)
+    $(call set-target-local-cflags-vars,debug)
+    LOCAL_SHARED_LIBRARIES += libdl
+    LOCAL_MULTILIB := both
+    # LOCAL_MODULE_PATH_32 := $(ART_TARGET_OUT)/$(ART_TARGET_ARCH_32)
+    # LOCAL_MODULE_PATH_64 := $(ART_TARGET_OUT)/$(ART_TARGET_ARCH_64)
+    LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH)
+    include $(BUILD_SHARED_LIBRARY)
+  else # host
+    LOCAL_CLANG := $(ART_HOST_CLANG)
+    LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS)
+    LOCAL_ASFLAGS := $(ART_HOST_ASFLAGS)
+    LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread
+    LOCAL_IS_HOST_MODULE := true
+    LOCAL_MULTILIB := both
+    include $(BUILD_HOST_SHARED_LIBRARY)
+  endif
+
+  # Clear locally used variables.
+  art_target_or_host :=
+endef
+
+ifeq ($(ART_BUILD_TARGET),true)
+  $(eval $(call build-libartbenchmark,target))
+endif
+ifeq ($(ART_BUILD_HOST),true)
+  $(eval $(call build-libartbenchmark,host))
+endif
+
+# Clear locally used variables.
+LOCAL_PATH :=
+LIBARTBENCHMARK_COMMON_SRC_FILES :=
diff --git a/test/999-jni-perf/info.txt b/benchmark/jni-perf/info.txt
similarity index 100%
rename from test/999-jni-perf/info.txt
rename to benchmark/jni-perf/info.txt
diff --git a/test/999-jni-perf/perf-jni.cc b/benchmark/jni-perf/perf_jni.cc
similarity index 71%
rename from test/999-jni-perf/perf-jni.cc
rename to benchmark/jni-perf/perf_jni.cc
index 51eeb83..cd8d520 100644
--- a/test/999-jni-perf/perf-jni.cc
+++ b/benchmark/jni-perf/perf_jni.cc
@@ -24,18 +24,14 @@
 
 namespace {
 
-extern "C" JNIEXPORT jint JNICALL Java_Main_perfJniEmptyCall(JNIEnv*, jobject) {
-  return 0;
+extern "C" JNIEXPORT void JNICALL Java_JniPerfBenchmark_perfJniEmptyCall(JNIEnv*, jobject) {}
+
+extern "C" JNIEXPORT void JNICALL Java_JniPerfBenchmark_perfSOACall(JNIEnv* env, jobject) {
+  ScopedObjectAccess soa(env);
 }
 
-extern "C" JNIEXPORT jint JNICALL Java_Main_perfSOACall(JNIEnv*, jobject) {
-  ScopedObjectAccess soa(Thread::Current());
-  return 0;
-}
-
-extern "C" JNIEXPORT jint JNICALL Java_Main_perfSOAUncheckedCall(JNIEnv*, jobject) {
+extern "C" JNIEXPORT void JNICALL Java_JniPerfBenchmark_perfSOAUncheckedCall(JNIEnv*, jobject) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  return 0;
 }
 
 }  // namespace
diff --git a/benchmark/jni-perf/src/JniPerfBenchmark.java b/benchmark/jni-perf/src/JniPerfBenchmark.java
new file mode 100644
index 0000000..b1b21ce
--- /dev/null
+++ b/benchmark/jni-perf/src/JniPerfBenchmark.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+import com.google.caliper.SimpleBenchmark;
+
+public class JniPerfBenchmark extends SimpleBenchmark {
+  private static final String MSG = "ABCDE";
+
+  native void perfJniEmptyCall();
+  native void perfSOACall();
+  native void perfSOAUncheckedCall();
+
+  public void timeFastJNI(int N) {
+    // TODO: This might be an intrinsic.
+    for (long i = 0; i < N; i++) {
+      char c = MSG.charAt(2);
+    }
+  }
+
+  public void timeEmptyCall(int N) {
+    for (long i = 0; i < N; i++) {
+      perfJniEmptyCall();
+    }
+  }
+
+  public void timeSOACall(int N) {
+    for (long i = 0; i < N; i++) {
+      perfSOACall();
+    }
+  }
+
+  public void timeSOAUncheckedCall(int N) {
+    for (long i = 0; i < N; i++) {
+      perfSOAUncheckedCall();
+    }
+  }
+
+  {
+    System.loadLibrary("artbenchmark");
+  }
+}
diff --git a/test/998-scoped-primitive-array/info.txt b/benchmark/scoped-primitive-array/info.txt
similarity index 100%
rename from test/998-scoped-primitive-array/info.txt
rename to benchmark/scoped-primitive-array/info.txt
diff --git a/benchmark/scoped-primitive-array/scoped_primitive_array.cc b/benchmark/scoped-primitive-array/scoped_primitive_array.cc
new file mode 100644
index 0000000..1664157
--- /dev/null
+++ b/benchmark/scoped-primitive-array/scoped_primitive_array.cc
@@ -0,0 +1,58 @@
+/*
+ * 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 "jni.h"
+#include "ScopedPrimitiveArray.h"
+
+extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureByteArray(
+    JNIEnv* env, jclass, int reps, jbyteArray arr) {
+  jlong ret = 0;
+  for (jint i = 0; i < reps; ++i) {
+    ScopedByteArrayRO sc(env, arr);
+    ret += sc[0] + sc[sc.size() - 1];
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureShortArray(
+    JNIEnv* env, jclass, int reps, jshortArray arr) {
+  jlong ret = 0;
+  for (jint i = 0; i < reps; ++i) {
+    ScopedShortArrayRO sc(env, arr);
+    ret += sc[0] + sc[sc.size() - 1];
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureIntArray(
+    JNIEnv* env, jclass, int reps, jintArray arr) {
+  jlong ret = 0;
+  for (jint i = 0; i < reps; ++i) {
+    ScopedIntArrayRO sc(env, arr);
+    ret += sc[0] + sc[sc.size() - 1];
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureLongArray(
+    JNIEnv* env, jclass, int reps, jlongArray arr) {
+  jlong ret = 0;
+  for (jint i = 0; i < reps; ++i) {
+    ScopedLongArrayRO sc(env, arr);
+    ret += sc[0] + sc[sc.size() - 1];
+  }
+  return ret;
+}
diff --git a/benchmark/scoped-primitive-array/src/ScopedPrimitiveArrayBenchmark.java b/benchmark/scoped-primitive-array/src/ScopedPrimitiveArrayBenchmark.java
new file mode 100644
index 0000000..be276fe
--- /dev/null
+++ b/benchmark/scoped-primitive-array/src/ScopedPrimitiveArrayBenchmark.java
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+import com.google.caliper.SimpleBenchmark; 
+
+public class ScopedPrimitiveArrayBenchmark extends SimpleBenchmark {
+  // Measure adds the first and last element of the array by using ScopedPrimitiveArray.
+  static native long measureByteArray(int reps, byte[] arr);
+  static native long measureShortArray(int reps, short[] arr);
+  static native long measureIntArray(int reps, int[] arr);
+  static native long measureLongArray(int reps, long[] arr);
+
+  static final int smallLength = 16;
+  static final int mediumLength = 256;
+  static final int largeLength = 8096;
+  static byte[] smallBytes = new byte[smallLength];
+  static byte[] mediumBytes = new byte[mediumLength];
+  static byte[] largeBytes = new byte[largeLength];
+  static short[] smallShorts = new short[smallLength];
+  static short[] mediumShorts = new short[mediumLength];
+  static short[] largeShorts = new short[largeLength];
+  static int[] smallInts = new int[smallLength];
+  static int[] mediumInts = new int[mediumLength];
+  static int[] largeInts = new int[largeLength];
+  static long[] smallLongs = new long[smallLength];
+  static long[] mediumLongs = new long[mediumLength];
+  static long[] largeLongs = new long[largeLength];
+
+  public void timeSmallBytes(int reps) {
+    measureByteArray(reps, smallBytes);
+  }
+
+  public void timeMediumBytes(int reps) {
+    measureByteArray(reps, mediumBytes);
+  }
+
+  public void timeLargeBytes(int reps) {
+    measureByteArray(reps, largeBytes);
+  }
+
+  public void timeSmallShorts(int reps) {
+    measureShortArray(reps, smallShorts);
+  }
+
+  public void timeMediumShorts(int reps) {
+    measureShortArray(reps, mediumShorts);
+  }
+
+  public void timeLargeShorts(int reps) {
+    measureShortArray(reps, largeShorts);
+  }
+
+  public void timeSmallInts(int reps) {
+    measureIntArray(reps, smallInts);
+  }
+
+  public void timeMediumInts(int reps) {
+    measureIntArray(reps, mediumInts);
+  }
+
+  public void timeLargeInts(int reps) {
+    measureIntArray(reps, largeInts);
+  }
+
+  public void timeSmallLongs(int reps) {
+    measureLongArray(reps, smallLongs);
+  }
+
+  public void timeMediumLongs(int reps) {
+    measureLongArray(reps, mediumLongs);
+  }
+
+  public void timeLargeLongs(int reps) {
+    measureLongArray(reps, largeLongs);
+  }
+
+  {
+    System.loadLibrary("artbenchmark");
+  }
+}
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index acce68b..a443487 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -348,16 +348,6 @@
 ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default $(art_host_cflags)
 ART_HOST_ASFLAGS += $(art_asflags)
 
-# Disable -Wpessimizing-move: triggered for art/runtime/base/variant_map.h:261
-# Adding this flag to art_clang_cflags doesn't work because -Wall gets added to
-# ART_HOST_CFLAGS (as a part of art_cflags) after
-# -Wno-pessimizing-move.  Instead, add the flag here to both
-# ART_TARGET_CLANG_CFLAGS and ART_HOST_CFLAGS
-ifeq ($(ART_HOST_CLANG),true)
-ART_HOST_CFLAGS += -Wno-pessimizing-move
-endif
-ART_TARGET_CLANG_CFLAGS += -Wno-pessimizing-move
-
 # The latest clang update trips over many of the files in art and never finishes
 # compiling for aarch64 with -O3 (or -O2). Drop back to -O1 while we investigate
 # to stop punishing the build server.
diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc
index c8aa990..f2c2e22 100644
--- a/compiler/dex/global_value_numbering_test.cc
+++ b/compiler/dex/global_value_numbering_test.cc
@@ -202,7 +202,7 @@
         for (size_t j = 0u; j != def->num_successors; ++j) {
           SuccessorBlockInfo* successor_block_info =
               static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
-                                                               kArenaAllocSuccessor));
+                                                               kArenaAllocSuccessors));
           successor_block_info->block = j;
           successor_block_info->key = 0u;  // Not used by class init check elimination.
           bb->successor_blocks.push_back(successor_block_info);
@@ -474,7 +474,7 @@
   BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
   check_bb->successor_block_list_type = kCatch;
   SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
-      (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+      (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors));
   successor_block_info->block = catch_handler->id;
   check_bb->successor_blocks.push_back(successor_block_info);
 }
@@ -2284,7 +2284,7 @@
   BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
   check_bb->successor_block_list_type = kCatch;
   SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
-      (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+      (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors));
   successor_block_info->block = catch_handler->id;
   check_bb->successor_blocks.push_back(successor_block_info);
   BasicBlock* merge_block = cu_.mir_graph->GetBasicBlock(4u);
diff --git a/compiler/dex/gvn_dead_code_elimination_test.cc b/compiler/dex/gvn_dead_code_elimination_test.cc
index 4df0a8b..28c61a8 100644
--- a/compiler/dex/gvn_dead_code_elimination_test.cc
+++ b/compiler/dex/gvn_dead_code_elimination_test.cc
@@ -209,7 +209,7 @@
         for (size_t j = 0u; j != def->num_successors; ++j) {
           SuccessorBlockInfo* successor_block_info =
               static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
-                                                               kArenaAllocSuccessor));
+                                                               kArenaAllocSuccessors));
           successor_block_info->block = j;
           successor_block_info->key = 0u;  // Not used by class init check elimination.
           bb->successor_blocks.push_back(successor_block_info);
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 7976a9a..4efe4af 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -572,7 +572,7 @@
     DCHECK(case_block != nullptr);
     SuccessorBlockInfo* successor_block_info =
         static_cast<SuccessorBlockInfo*>(arena_->Alloc(sizeof(SuccessorBlockInfo),
-                                                       kArenaAllocSuccessor));
+                                                       kArenaAllocSuccessors));
     successor_block_info->block = case_block->id;
     successor_block_info->key =
         (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
@@ -627,7 +627,7 @@
         catches_.insert(catch_block->start_offset);
       }
       SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
-          (arena_->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+          (arena_->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors));
       successor_block_info->block = catch_block->id;
       successor_block_info->key = iterator.GetHandlerTypeIndex();
       cur_block->successor_blocks.push_back(successor_block_info);
@@ -2178,7 +2178,7 @@
     result_bb->successor_blocks.reserve(successor_blocks.size());
     for (SuccessorBlockInfo* sbi_old : successor_blocks) {
       SuccessorBlockInfo* sbi_new = static_cast<SuccessorBlockInfo*>(
-          arena->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+          arena->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors));
       memcpy(sbi_new, sbi_old, sizeof(SuccessorBlockInfo));
       result_bb->successor_blocks.push_back(sbi_new);
     }
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 1df6a4f..097abdc 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -379,7 +379,7 @@
         terminated_by_return(), dominates_return(), use_lvn(), first_mir_insn(),
         last_mir_insn(), data_flow_info(), dominators(), i_dominated(), dom_frontier(),
         predecessors(allocator->Adapter(kArenaAllocBBPredecessors)),
-        successor_blocks(allocator->Adapter(kArenaAllocSuccessor)) {
+        successor_blocks(allocator->Adapter(kArenaAllocSuccessors)) {
   }
   BasicBlockId id;
   BasicBlockId dfs_id;
diff --git a/compiler/dex/mir_graph_test.cc b/compiler/dex/mir_graph_test.cc
index 49b7511..7858681 100644
--- a/compiler/dex/mir_graph_test.cc
+++ b/compiler/dex/mir_graph_test.cc
@@ -79,7 +79,7 @@
         for (size_t j = 0u; j != def->num_successors; ++j) {
           SuccessorBlockInfo* successor_block_info =
               static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
-                                                               kArenaAllocSuccessor));
+                                                               kArenaAllocSuccessors));
           successor_block_info->block = j;
           successor_block_info->key = 0u;  // Not used by class init check elimination.
           bb->successor_blocks.push_back(successor_block_info);
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index 47123ba..a0cedff 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -118,7 +118,7 @@
         for (size_t j = 0u; j != def->num_successors; ++j) {
           SuccessorBlockInfo* successor_block_info =
               static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
-                                                               kArenaAllocSuccessor));
+                                                               kArenaAllocSuccessors));
           successor_block_info->block = j;
           successor_block_info->key = 0u;  // Not used by class init check elimination.
           bb->successor_blocks.push_back(successor_block_info);
@@ -244,7 +244,7 @@
     BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
     check_bb->successor_block_list_type = kCatch;
     SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
-        (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+        (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors));
     successor_block_info->block = catch_handler->id;
     check_bb->successor_blocks.push_back(successor_block_info);
   }
diff --git a/compiler/dex/type_inference_test.cc b/compiler/dex/type_inference_test.cc
index 872a8d6..528a18c 100644
--- a/compiler/dex/type_inference_test.cc
+++ b/compiler/dex/type_inference_test.cc
@@ -322,7 +322,7 @@
         for (size_t j = 0u; j != def->num_successors; ++j) {
           SuccessorBlockInfo* successor_block_info =
               static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
-                                                               kArenaAllocSuccessor));
+                                                               kArenaAllocSuccessors));
           successor_block_info->block = j;
           successor_block_info->key = 0u;  // Not used by class init check elimination.
           bb->successor_blocks.push_back(successor_block_info);
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
index 84201c3..b0e83b0 100644
--- a/compiler/optimizing/boolean_simplifier.cc
+++ b/compiler/optimizing/boolean_simplifier.cc
@@ -42,9 +42,9 @@
 // successor and the successor can only be reached from them.
 static bool BlocksDoMergeTogether(HBasicBlock* block1, HBasicBlock* block2) {
   if (!block1->IsSingleGoto() || !block2->IsSingleGoto()) return false;
-  HBasicBlock* succ1 = block1->GetSuccessors().Get(0);
-  HBasicBlock* succ2 = block2->GetSuccessors().Get(0);
-  return succ1 == succ2 && succ1->GetPredecessors().Size() == 2u;
+  HBasicBlock* succ1 = block1->GetSuccessor(0);
+  HBasicBlock* succ2 = block2->GetSuccessor(0);
+  return succ1 == succ2 && succ1->GetPredecessors().size() == 2u;
 }
 
 // Returns true if the outcome of the branching matches the boolean value of
@@ -108,7 +108,7 @@
   if (!BlocksDoMergeTogether(true_block, false_block)) {
     return;
   }
-  HBasicBlock* merge_block = true_block->GetSuccessors().Get(0);
+  HBasicBlock* merge_block = true_block->GetSuccessor(0);
   if (!merge_block->HasSinglePhi()) {
     return;
   }
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index ebc0adc..70ad408 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -275,9 +275,8 @@
       // Loop header of loop_info. Exiting loop is normal.
       return false;
     }
-    const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors();
-    for (size_t i = 0; i < successors.Size(); i++) {
-      if (!loop_info->Contains(*successors.Get(i))) {
+    for (HBasicBlock* successor : block->GetSuccessors()) {
+      if (!loop_info->Contains(*successor)) {
         // One of the successors exits the loop.
         return true;
       }
@@ -797,8 +796,8 @@
     HBasicBlock* new_pre_header = header->GetDominator();
     DCHECK(new_pre_header == header->GetLoopInformation()->GetPreHeader());
     HBasicBlock* if_block = new_pre_header->GetDominator();
-    HBasicBlock* dummy_block = if_block->GetSuccessors().Get(0);  // True successor.
-    HBasicBlock* deopt_block = if_block->GetSuccessors().Get(1);  // False successor.
+    HBasicBlock* dummy_block = if_block->GetSuccessor(0);  // True successor.
+    HBasicBlock* deopt_block = if_block->GetSuccessor(1);  // False successor.
 
     dummy_block->AddInstruction(new (graph->GetArena()) HGoto());
     deopt_block->AddInstruction(new (graph->GetArena()) HGoto());
@@ -845,14 +844,14 @@
     DCHECK(header->IsLoopHeader());
     HBasicBlock* pre_header = header->GetDominator();
     if (loop_entry_test_block_added) {
-      DCHECK(deopt_block->GetSuccessors().Get(0) == pre_header);
+      DCHECK(deopt_block->GetSuccessor(0) == pre_header);
     } else {
       DCHECK(deopt_block == pre_header);
     }
     HGraph* graph = header->GetGraph();
     HSuspendCheck* suspend_check = header->GetLoopInformation()->GetSuspendCheck();
     if (loop_entry_test_block_added) {
-      DCHECK_EQ(deopt_block, header->GetDominator()->GetDominator()->GetSuccessors().Get(1));
+      DCHECK_EQ(deopt_block, header->GetDominator()->GetDominator()->GetSuccessor(1));
     }
 
     HIntConstant* const_instr = graph->GetIntConstant(constant);
@@ -926,7 +925,7 @@
     DCHECK(header->IsLoopHeader());
     HBasicBlock* pre_header = header->GetDominator();
     if (loop_entry_test_block_added) {
-      DCHECK(deopt_block->GetSuccessors().Get(0) == pre_header);
+      DCHECK(deopt_block->GetSuccessor(0) == pre_header);
     } else {
       DCHECK(deopt_block == pre_header);
     }
@@ -1256,11 +1255,11 @@
 
     HBasicBlock* true_successor = instruction->IfTrueSuccessor();
     // There should be no critical edge at this point.
-    DCHECK_EQ(true_successor->GetPredecessors().Size(), 1u);
+    DCHECK_EQ(true_successor->GetPredecessors().size(), 1u);
 
     HBasicBlock* false_successor = instruction->IfFalseSuccessor();
     // There should be no critical edge at this point.
-    DCHECK_EQ(false_successor->GetPredecessors().Size(), 1u);
+    DCHECK_EQ(false_successor->GetPredecessors().size(), 1u);
 
     ValueRange* left_range = LookupValueRange(left, block);
     MonotonicValueRange* left_monotonic_range = nullptr;
@@ -1468,10 +1467,10 @@
     // Start with input 1. Input 0 is from the incoming block.
     HInstruction* input1 = phi->InputAt(1);
     DCHECK(phi->GetBlock()->GetLoopInformation()->IsBackEdge(
-        *phi->GetBlock()->GetPredecessors().Get(1)));
+        *phi->GetBlock()->GetPredecessor(1)));
     for (size_t i = 2, e = phi->InputCount(); i < e; ++i) {
       DCHECK(phi->GetBlock()->GetLoopInformation()->IsBackEdge(
-          *phi->GetBlock()->GetPredecessors().Get(i)));
+          *phi->GetBlock()->GetPredecessor(i)));
       if (input1 != phi->InputAt(i)) {
         return false;
       }
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 7a3aa58..0a3f083 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -398,8 +398,8 @@
 
     // Find predecessors which are not covered by the same TryItem range. Such
     // edges enter the try block and will have a TryBoundary inserted.
-    for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) {
-      HBasicBlock* predecessor = try_block->GetPredecessors().Get(i);
+    for (size_t i = 0; i < try_block->GetPredecessors().size(); ++i) {
+      HBasicBlock* predecessor = try_block->GetPredecessor(i);
       if (predecessor->IsSingleTryBoundary()) {
         // The edge was already split because of an exit from a neighbouring
         // TryItem. We split it again and insert an entry point.
@@ -426,8 +426,7 @@
 
     // Find successors which are not covered by the same TryItem range. Such
     // edges exit the try block and will have a TryBoundary inserted.
-    for (size_t i = 0; i < try_block->GetSuccessors().Size(); ++i) {
-      HBasicBlock* successor = try_block->GetSuccessors().Get(i);
+    for (HBasicBlock* successor : try_block->GetSuccessors()) {
       if (successor->IsCatchBlock()) {
         // A catch block is always considered an entry point into its TryItem.
         // We therefore assume this is an exit point, regardless of whether
@@ -479,6 +478,8 @@
   graph_->SetEntryBlock(entry_block_);
   graph_->SetExitBlock(exit_block_);
 
+  graph_->SetHasTryCatch(code_item.tries_size_ != 0);
+
   InitializeLocals(code_item.registers_size_);
   graph_->SetMaximumNumberOfOutVRegs(code_item.outs_size_);
 
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 1097adb..3bbff6a 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -171,7 +171,7 @@
 
 HBasicBlock* CodeGenerator::FirstNonEmptyBlock(HBasicBlock* block) const {
   while (block->IsSingleJump()) {
-    block = block->GetSuccessors().Get(0);
+    block = block->GetSuccessor(0);
   }
   return block;
 }
@@ -248,6 +248,12 @@
 
   GenerateSlowPaths();
 
+  // Emit catch stack maps at the end of the stack map stream as expected by the
+  // runtime exception handler.
+  if (!is_baseline && graph_->HasTryCatch()) {
+    RecordCatchBlockInfo();
+  }
+
   // Finalize instructions in assember;
   Finalize(allocator);
 }
@@ -805,6 +811,73 @@
   stack_map_stream_.EndStackMapEntry();
 }
 
+void CodeGenerator::RecordCatchBlockInfo() {
+  ArenaAllocator* arena = graph_->GetArena();
+
+  for (size_t i = 0, e = block_order_->Size(); i < e; ++i) {
+    HBasicBlock* block = block_order_->Get(i);
+    if (!block->IsCatchBlock()) {
+      continue;
+    }
+
+    uint32_t dex_pc = block->GetDexPc();
+    uint32_t num_vregs = graph_->GetNumberOfVRegs();
+    uint32_t inlining_depth = 0;  // Inlining of catch blocks is not supported at the moment.
+    uint32_t native_pc = GetAddressOf(block);
+    uint32_t register_mask = 0;   // Not used.
+
+    // The stack mask is not used, so we leave it empty.
+    ArenaBitVector* stack_mask = new (arena) ArenaBitVector(arena, 0, /* expandable */ true);
+
+    stack_map_stream_.BeginStackMapEntry(dex_pc,
+                                         native_pc,
+                                         register_mask,
+                                         stack_mask,
+                                         num_vregs,
+                                         inlining_depth);
+
+    HInstruction* current_phi = block->GetFirstPhi();
+    for (size_t vreg = 0; vreg < num_vregs; ++vreg) {
+    while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) {
+      HInstruction* next_phi = current_phi->GetNext();
+      DCHECK(next_phi == nullptr ||
+             current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber())
+          << "Phis need to be sorted by vreg number to keep this a linear-time loop.";
+      current_phi = next_phi;
+    }
+
+      if (current_phi == nullptr || current_phi->AsPhi()->GetRegNumber() != vreg) {
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0);
+      } else {
+        Location location = current_phi->GetLiveInterval()->ToLocation();
+        switch (location.GetKind()) {
+          case Location::kStackSlot: {
+            stack_map_stream_.AddDexRegisterEntry(
+                DexRegisterLocation::Kind::kInStack, location.GetStackIndex());
+            break;
+          }
+          case Location::kDoubleStackSlot: {
+            stack_map_stream_.AddDexRegisterEntry(
+                DexRegisterLocation::Kind::kInStack, location.GetStackIndex());
+            stack_map_stream_.AddDexRegisterEntry(
+                DexRegisterLocation::Kind::kInStack, location.GetHighStackIndex(kVRegSize));
+            ++vreg;
+            DCHECK_LT(vreg, num_vregs);
+            break;
+          }
+          default: {
+            // All catch phis must be allocated to a stack slot.
+            LOG(FATAL) << "Unexpected kind " << location.GetKind();
+            UNREACHABLE();
+          }
+        }
+      }
+    }
+
+    stack_map_stream_.EndStackMapEntry();
+  }
+}
+
 void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slow_path) {
   if (environment == nullptr) return;
 
@@ -975,6 +1048,13 @@
   }
 }
 
+bool CodeGenerator::IsImplicitNullCheckAllowed(HNullCheck* null_check) const {
+  return compiler_options_.GetImplicitNullChecks() &&
+         // Null checks which might throw into a catch block need to save live
+         // registers and therefore cannot be done implicitly.
+         !null_check->CanThrowIntoCatchBlock();
+}
+
 bool CodeGenerator::CanMoveNullCheckToUser(HNullCheck* null_check) {
   HInstruction* first_next_not_move = null_check->GetNextDisregardingMoves();
 
@@ -990,10 +1070,6 @@
     return;
   }
 
-  if (!compiler_options_.GetImplicitNullChecks()) {
-    return;
-  }
-
   if (!instr->CanDoImplicitNullCheckOn(instr->InputAt(0))) {
     return;
   }
@@ -1005,9 +1081,11 @@
   // and needs to record the pc.
   if (first_prev_not_move != nullptr && first_prev_not_move->IsNullCheck()) {
     HNullCheck* null_check = first_prev_not_move->AsNullCheck();
-    // TODO: The parallel moves modify the environment. Their changes need to be reverted
-    // otherwise the stack maps at the throw point will not be correct.
-    RecordPcInfo(null_check, null_check->GetDexPc());
+    if (IsImplicitNullCheckAllowed(null_check)) {
+      // TODO: The parallel moves modify the environment. Their changes need to be
+      // reverted otherwise the stack maps at the throw point will not be correct.
+      RecordPcInfo(null_check, null_check->GetDexPc());
+    }
   }
 }
 
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index b3c4d72..a93d07a 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -237,6 +237,17 @@
   bool CanMoveNullCheckToUser(HNullCheck* null_check);
   void MaybeRecordImplicitNullCheck(HInstruction* instruction);
 
+  // Records a stack map which the runtime might use to set catch phi values
+  // during exception delivery.
+  // TODO: Replace with a catch-entering instruction that records the environment.
+  void RecordCatchBlockInfo();
+
+  // Returns true if implicit null checks are allowed in the compiler options
+  // and if the null check is not inside a try block. We currently cannot do
+  // implicit null checks in that case because we need the NullCheckSlowPath to
+  // save live registers, which may be needed by the runtime to set catch phis.
+  bool IsImplicitNullCheckAllowed(HNullCheck* null_check) const;
+
   void AddSlowPath(SlowPathCode* slow_path) {
     slow_paths_.Add(slow_path);
   }
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index a4c58b0..b3e38f0 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -66,6 +66,10 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     arm_codegen->InvokeRuntime(
         QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this);
   }
@@ -86,6 +90,10 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     arm_codegen->InvokeRuntime(
         QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this);
   }
@@ -150,6 +158,10 @@
     LocationSummary* locations = instruction_->GetLocations();
 
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     // We're moving two locations to locations that could overlap, so we need a parallel
     // move resolver.
     InvokeRuntimeCallingConvention calling_convention;
@@ -2741,8 +2753,10 @@
 }
 
 void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
   if (instruction->HasUses()) {
     locations->SetOut(Location::SameAsFirstInput());
@@ -3495,8 +3509,10 @@
 }
 
 void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   if (instruction->HasUses()) {
     locations->SetOut(Location::SameAsFirstInput());
@@ -3524,7 +3540,7 @@
 }
 
 void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
-  if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+  if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
     GenerateImplicitNullCheck(instruction);
   } else {
     GenerateExplicitNullCheck(instruction);
@@ -3863,8 +3879,10 @@
 }
 
 void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   if (instruction->HasUses()) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 6b1457b..5094f67 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -198,6 +198,10 @@
     CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
 
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     // We're moving two locations to locations that could overlap, so we need a parallel
     // move resolver.
     InvokeRuntimeCallingConvention calling_convention;
@@ -226,6 +230,10 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     arm64_codegen->InvokeRuntime(
         QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
@@ -338,6 +346,10 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     arm64_codegen->InvokeRuntime(
         QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
@@ -1580,8 +1592,10 @@
 }
 
 void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction));
   if (instruction->HasUses()) {
@@ -1977,8 +1991,10 @@
 }
 
 void LocationsBuilderARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
   if (instruction->HasUses()) {
     locations->SetOut(Location::SameAsFirstInput());
@@ -2875,8 +2891,10 @@
 }
 
 void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   if (instruction->HasUses()) {
     locations->SetOut(Location::SameAsFirstInput());
@@ -2905,7 +2923,7 @@
 }
 
 void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
-  if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+  if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
     GenerateImplicitNullCheck(instruction);
   } else {
     GenerateExplicitNullCheck(instruction);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 10942ef..8d60026 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -118,6 +118,10 @@
     LocationSummary* locations = instruction_->GetLocations();
     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     // We're moving two locations to locations that could overlap, so we need a parallel
     // move resolver.
     InvokeRuntimeCallingConvention calling_convention;
@@ -151,6 +155,10 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowDivZero),
                                   instruction_,
                                   instruction_->GetDexPc(),
@@ -269,6 +277,10 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowNullPointer),
                                   instruction_,
                                   instruction_->GetDexPc(),
@@ -1566,8 +1578,10 @@
 }
 
 void LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   if (instruction->HasUses()) {
@@ -1862,8 +1876,10 @@
 }
 
 void LocationsBuilderMIPS64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
   if (instruction->HasUses()) {
     locations->SetOut(Location::SameAsFirstInput());
@@ -2824,8 +2840,10 @@
 }
 
 void LocationsBuilderMIPS64::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   if (instruction->HasUses()) {
     locations->SetOut(Location::SameAsFirstInput());
@@ -2852,7 +2870,7 @@
 }
 
 void InstructionCodeGeneratorMIPS64::VisitNullCheck(HNullCheck* instruction) {
-  if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+  if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
     GenerateImplicitNullCheck(instruction);
   } else {
     GenerateExplicitNullCheck(instruction);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index a5ad226..dc5c86e 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -56,6 +56,10 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowNullPointer),
                                instruction_,
                                instruction_->GetDexPc(),
@@ -78,6 +82,10 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowDivZero),
                                instruction_,
                                instruction_->GetDexPc(),
@@ -125,6 +133,10 @@
     __ Bind(GetEntryLabel());
     // We're moving two locations to locations that could overlap, so we need a parallel
     // move resolver.
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     InvokeRuntimeCallingConvention calling_convention;
     x86_codegen->EmitParallelMoves(
         locations->InAt(0),
@@ -3039,8 +3051,10 @@
 }
 
 void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   switch (instruction->GetType()) {
     case Primitive::kPrimByte:
     case Primitive::kPrimChar:
@@ -3984,9 +3998,11 @@
 }
 
 void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  Location loc = codegen_->IsImplicitNullCheckAllowed(instruction)
       ? Location::RequiresRegister()
       : Location::Any();
   locations->SetInAt(0, loc);
@@ -4019,7 +4035,7 @@
     __ cmpl(Address(ESP, obj.GetStackIndex()), Immediate(0));
   } else {
     DCHECK(obj.IsConstant()) << obj;
-    DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0);
+    DCHECK(obj.GetConstant()->IsNullConstant());
     __ jmp(slow_path->GetEntryLabel());
     return;
   }
@@ -4027,7 +4043,7 @@
 }
 
 void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
-  if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+  if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
     GenerateImplicitNullCheck(instruction);
   } else {
     GenerateExplicitNullCheck(instruction);
@@ -4432,8 +4448,10 @@
 }
 
 void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   if (instruction->HasUses()) {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 0f3eb74..0cf1089 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -57,6 +57,10 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     x64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowNullPointer),
                                instruction_,
                                instruction_->GetDexPc(),
@@ -79,6 +83,10 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     x64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowDivZero),
                                instruction_,
                                instruction_->GetDexPc(),
@@ -177,6 +185,10 @@
     LocationSummary* locations = instruction_->GetLocations();
     CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
     __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
     // We're moving two locations to locations that could overlap, so we need a parallel
     // move resolver.
     InvokeRuntimeCallingConvention calling_convention;
@@ -3194,8 +3206,10 @@
 }
 
 void LocationsBuilderX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::Any());
   if (instruction->HasUses()) {
     locations->SetOut(Location::SameAsFirstInput());
@@ -3748,9 +3762,11 @@
 }
 
 void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  Location loc = codegen_->IsImplicitNullCheckAllowed(instruction)
       ? Location::RequiresRegister()
       : Location::Any();
   locations->SetInAt(0, loc);
@@ -3783,7 +3799,7 @@
     __ cmpl(Address(CpuRegister(RSP), obj.GetStackIndex()), Immediate(0));
   } else {
     DCHECK(obj.IsConstant()) << obj;
-    DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0);
+    DCHECK(obj.GetConstant()->IsNullConstant());
     __ jmp(slow_path->GetEntryLabel());
     return;
   }
@@ -3791,7 +3807,7 @@
 }
 
 void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
-  if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+  if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
     GenerateImplicitNullCheck(instruction);
   } else {
     GenerateExplicitNullCheck(instruction);
@@ -4175,8 +4191,10 @@
 }
 
 void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   if (instruction->HasUses()) {
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 4fbb51d..72c67f5 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -561,7 +561,7 @@
   ASSERT_FALSE(equal->NeedsMaterialization());
 
   auto hook_before_codegen = [](HGraph* graph_in) {
-    HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0);
+    HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessor(0);
     HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
     block->InsertInstructionBefore(move, block->GetLastInstruction());
   };
@@ -667,7 +667,7 @@
     code_block->AddInstruction(&ret);
 
     auto hook_before_codegen = [](HGraph* graph_in) {
-      HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0);
+      HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessor(0);
       HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
       block->InsertInstructionBefore(move, block->GetLastInstruction());
     };
@@ -733,7 +733,7 @@
     if_false_block->AddInstruction(&ret_ge);
 
     auto hook_before_codegen = [](HGraph* graph_in) {
-      HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0);
+      HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessor(0);
       HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
       block->InsertInstructionBefore(move, block->GetLastInstruction());
     };
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 50cbf5c..509478c 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -42,8 +42,8 @@
       MarkReachableBlocks(if_instruction->IfFalseSuccessor(), visited);
     }
   } else {
-    for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) {
-      MarkReachableBlocks(block->GetSuccessors().Get(i), visited);
+    for (HBasicBlock* successor : block->GetSuccessors()) {
+      MarkReachableBlocks(successor, visited);
     }
   }
 }
@@ -99,12 +99,12 @@
   // Connect successive blocks created by dead branches. Order does not matter.
   for (HReversePostOrderIterator it(*graph_); !it.Done();) {
     HBasicBlock* block  = it.Current();
-    if (block->IsEntryBlock() || block->GetSuccessors().Size() != 1u) {
+    if (block->IsEntryBlock() || block->GetSuccessors().size() != 1u) {
       it.Advance();
       continue;
     }
-    HBasicBlock* successor = block->GetSuccessors().Get(0);
-    if (successor->IsExitBlock() || successor->GetPredecessors().Size() != 1u) {
+    HBasicBlock* successor = block->GetSuccessor(0);
+    if (successor->IsExitBlock() || successor->GetPredecessors().size() != 1u) {
       it.Advance();
       continue;
     }
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 847d5a4..074ed71 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -29,19 +29,16 @@
   current_block_ = block;
 
   // Check consistency with respect to predecessors of `block`.
-  const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
   std::map<HBasicBlock*, size_t> predecessors_count;
-  for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
-    HBasicBlock* p = predecessors.Get(i);
+  for (HBasicBlock* p : block->GetPredecessors()) {
     ++predecessors_count[p];
   }
   for (auto& pc : predecessors_count) {
     HBasicBlock* p = pc.first;
     size_t p_count_in_block_predecessors = pc.second;
-    const GrowableArray<HBasicBlock*>& p_successors = p->GetSuccessors();
     size_t block_count_in_p_successors = 0;
-    for (size_t j = 0, f = p_successors.Size(); j < f; ++j) {
-      if (p_successors.Get(j) == block) {
+    for (HBasicBlock* p_successor : p->GetSuccessors()) {
+      if (p_successor == block) {
         ++block_count_in_p_successors;
       }
     }
@@ -55,19 +52,16 @@
   }
 
   // Check consistency with respect to successors of `block`.
-  const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors();
   std::map<HBasicBlock*, size_t> successors_count;
-  for (size_t i = 0, e = successors.Size(); i < e; ++i) {
-    HBasicBlock* s = successors.Get(i);
+  for (HBasicBlock* s : block->GetSuccessors()) {
     ++successors_count[s];
   }
   for (auto& sc : successors_count) {
     HBasicBlock* s = sc.first;
     size_t s_count_in_block_successors = sc.second;
-    const GrowableArray<HBasicBlock*>& s_predecessors = s->GetPredecessors();
     size_t block_count_in_s_predecessors = 0;
-    for (size_t j = 0, f = s_predecessors.Size(); j < f; ++j) {
-      if (s_predecessors.Get(j) == block) {
+    for (HBasicBlock* s_predecessor : s->GetPredecessors()) {
+      if (s_predecessor == block) {
         ++block_count_in_s_predecessors;
       }
     }
@@ -92,8 +86,7 @@
   // Ensure that only Return(Void) and Throw jump to Exit. An exiting
   // TryBoundary may be between a Throw and the Exit if the Throw is in a try.
   if (block->IsExitBlock()) {
-    for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
-      HBasicBlock* predecessor = block->GetPredecessors().Get(i);
+    for (HBasicBlock* predecessor : block->GetPredecessors()) {
       if (predecessor->IsSingleTryBoundary()
           && !predecessor->GetLastInstruction()->AsTryBoundary()->IsEntry()) {
         HBasicBlock* real_predecessor = predecessor->GetSinglePredecessor();
@@ -178,8 +171,7 @@
                             try_boundary->GetId(),
                             handler->GetBlockId()));
     }
-    if (current_block_->GetSuccessors().Contains(
-            handler, /* start_from */ it.CurrentSuccessorIndex() + 1)) {
+    if (current_block_->HasSuccessor(handler, it.CurrentSuccessorIndex() + 1)) {
       AddError(StringPrintf("Exception handler block %d of %s:%d is listed multiple times.",
                             handler->GetBlockId(),
                             try_boundary->DebugName(),
@@ -359,15 +351,15 @@
   // never exceptional successors.
   const size_t num_normal_successors = block->NumberOfNormalSuccessors();
   for (size_t j = 0; j < num_normal_successors; ++j) {
-    HBasicBlock* successor = block->GetSuccessors().Get(j);
+    HBasicBlock* successor = block->GetSuccessor(j);
     if (successor->IsCatchBlock()) {
       AddError(StringPrintf("Catch block %d is a normal successor of block %d.",
                             successor->GetBlockId(),
                             block->GetBlockId()));
     }
   }
-  for (size_t j = num_normal_successors, e = block->GetSuccessors().Size(); j < e; ++j) {
-    HBasicBlock* successor = block->GetSuccessors().Get(j);
+  for (size_t j = num_normal_successors, e = block->GetSuccessors().size(); j < e; ++j) {
+    HBasicBlock* successor = block->GetSuccessor(j);
     if (!successor->IsCatchBlock()) {
       AddError(StringPrintf("Normal block %d is an exceptional successor of block %d.",
                             successor->GetBlockId(),
@@ -381,8 +373,8 @@
   // not accounted for.
   if (block->NumberOfNormalSuccessors() > 1) {
     for (size_t j = 0, e = block->NumberOfNormalSuccessors(); j < e; ++j) {
-      HBasicBlock* successor = block->GetSuccessors().Get(j);
-      if (successor->GetPredecessors().Size() > 1) {
+      HBasicBlock* successor = block->GetSuccessor(j);
+      if (successor->GetPredecessors().size() > 1) {
         AddError(StringPrintf("Critical edge between blocks %d and %d.",
                               block->GetBlockId(),
                               successor->GetBlockId()));
@@ -390,17 +382,6 @@
     }
   }
 
-  // Check Phi uniqueness (no two Phis with the same type refer to the same register).
-  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
-    HPhi* phi = it.Current()->AsPhi();
-    if (phi->GetNextEquivalentPhiWithSameType() != nullptr) {
-      std::stringstream type_str;
-      type_str << phi->GetType();
-      AddError(StringPrintf("Equivalent phi (%d) found for VReg %d with type: %s",
-          phi->GetId(), phi->GetRegNumber(), type_str.str().c_str()));
-    }
-  }
-
   // Ensure try membership information is consistent.
   if (block->IsCatchBlock()) {
     if (block->IsTryBlock()) {
@@ -417,8 +398,7 @@
                             block->GetBlockId()));
     }
   } else {
-    for (size_t i = 0; i < block->GetPredecessors().Size(); ++i) {
-      HBasicBlock* predecessor = block->GetPredecessors().Get(i);
+    for (HBasicBlock* predecessor : block->GetPredecessors()) {
       const HTryBoundary* incoming_try_entry = predecessor->ComputeTryEntryOfSuccessors();
       if (block->IsTryBlock()) {
         const HTryBoundary& stored_try_entry = block->GetTryCatchInformation()->GetTryEntry();
@@ -469,21 +449,21 @@
 
   // Ensure the loop header has only one incoming branch and the remaining
   // predecessors are back edges.
-  size_t num_preds = loop_header->GetPredecessors().Size();
+  size_t num_preds = loop_header->GetPredecessors().size();
   if (num_preds < 2) {
     AddError(StringPrintf(
         "Loop header %d has less than two predecessors: %zu.",
         id,
         num_preds));
   } else {
-    HBasicBlock* first_predecessor = loop_header->GetPredecessors().Get(0);
+    HBasicBlock* first_predecessor = loop_header->GetPredecessor(0);
     if (loop_information->IsBackEdge(*first_predecessor)) {
       AddError(StringPrintf(
           "First predecessor of loop header %d is a back edge.",
           id));
     }
-    for (size_t i = 1, e = loop_header->GetPredecessors().Size(); i < e; ++i) {
-      HBasicBlock* predecessor = loop_header->GetPredecessors().Get(i);
+    for (size_t i = 1, e = loop_header->GetPredecessors().size(); i < e; ++i) {
+      HBasicBlock* predecessor = loop_header->GetPredecessor(i);
       if (!loop_information->IsBackEdge(*predecessor)) {
         AddError(StringPrintf(
             "Loop header %d has multiple incoming (non back edge) blocks.",
@@ -586,6 +566,35 @@
   }
 }
 
+static bool IsSameSizeConstant(HInstruction* insn1, HInstruction* insn2) {
+  return insn1->IsConstant()
+      && insn2->IsConstant()
+      && Primitive::Is64BitType(insn1->GetType()) == Primitive::Is64BitType(insn2->GetType());
+}
+
+static bool IsConstantEquivalent(HInstruction* insn1, HInstruction* insn2, BitVector* visited) {
+  if (insn1->IsPhi() &&
+      insn1->AsPhi()->IsVRegEquivalentOf(insn2) &&
+      insn1->InputCount() == insn2->InputCount()) {
+    // Testing only one of the two inputs for recursion is sufficient.
+    if (visited->IsBitSet(insn1->GetId())) {
+      return true;
+    }
+    visited->SetBit(insn1->GetId());
+
+    for (size_t i = 0, e = insn1->InputCount(); i < e; ++i) {
+      if (!IsConstantEquivalent(insn1->InputAt(i), insn2->InputAt(i), visited)) {
+        return false;
+      }
+    }
+    return true;
+  } else if (IsSameSizeConstant(insn1, insn2)) {
+    return insn1->AsConstant()->GetValueAsUint64() == insn2->AsConstant()->GetValueAsUint64();
+  } else {
+    return false;
+  }
+}
+
 void SSAChecker::VisitPhi(HPhi* phi) {
   VisitInstruction(phi);
 
@@ -621,20 +630,19 @@
   } else {
     // Ensure the number of inputs of a non-catch phi is the same as the number
     // of its predecessors.
-    const GrowableArray<HBasicBlock*>& predecessors =
-        phi->GetBlock()->GetPredecessors();
-    if (phi->InputCount() != predecessors.Size()) {
+    const ArenaVector<HBasicBlock*>& predecessors = phi->GetBlock()->GetPredecessors();
+    if (phi->InputCount() != predecessors.size()) {
       AddError(StringPrintf(
           "Phi %d in block %d has %zu inputs, "
           "but block %d has %zu predecessors.",
           phi->GetId(), phi->GetBlock()->GetBlockId(), phi->InputCount(),
-          phi->GetBlock()->GetBlockId(), predecessors.Size()));
+          phi->GetBlock()->GetBlockId(), predecessors.size()));
     } else {
       // Ensure phi input at index I either comes from the Ith
       // predecessor or from a block that dominates this predecessor.
       for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
         HInstruction* input = phi->InputAt(i);
-        HBasicBlock* predecessor = predecessors.Get(i);
+        HBasicBlock* predecessor = predecessors[i];
         if (!(input->GetBlock() == predecessor
               || input->GetBlock()->Dominates(predecessor))) {
           AddError(StringPrintf(
@@ -646,6 +654,45 @@
       }
     }
   }
+
+  // Ensure that catch phis are sorted by their vreg number, as required by
+  // the register allocator and code generator. This does not apply to normal
+  // phis which can be constructed artifically.
+  if (phi->IsCatchPhi()) {
+    HInstruction* next_phi = phi->GetNext();
+    if (next_phi != nullptr && phi->GetRegNumber() > next_phi->AsPhi()->GetRegNumber()) {
+      AddError(StringPrintf("Catch phis %d and %d in block %d are not sorted by their "
+                            "vreg numbers.",
+                            phi->GetId(),
+                            next_phi->GetId(),
+                            phi->GetBlock()->GetBlockId()));
+    }
+  }
+
+  // Test phi equivalents. There should not be two of the same type and they
+  // should only be created for constants which were untyped in DEX.
+  for (HInstructionIterator phi_it(phi->GetBlock()->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+    HPhi* other_phi = phi_it.Current()->AsPhi();
+    if (phi != other_phi && phi->GetRegNumber() == other_phi->GetRegNumber()) {
+      if (phi->GetType() == other_phi->GetType()) {
+        std::stringstream type_str;
+        type_str << phi->GetType();
+        AddError(StringPrintf("Equivalent phi (%d) found for VReg %d with type: %s.",
+                              phi->GetId(),
+                              phi->GetRegNumber(),
+                              type_str.str().c_str()));
+      } else {
+        ArenaBitVector visited(GetGraph()->GetArena(), 0, /* expandable */ true);
+        if (!IsConstantEquivalent(phi, other_phi, &visited)) {
+          AddError(StringPrintf("Two phis (%d and %d) found for VReg %d but they "
+                                "are not equivalents of constants.",
+                                phi->GetId(),
+                                other_phi->GetId(),
+                                phi->GetRegNumber()));
+        }
+      }
+    }
+  }
 }
 
 void SSAChecker::HandleBooleanInput(HInstruction* instruction, size_t input_index) {
diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc
index 59d5092..7968e88 100644
--- a/compiler/optimizing/graph_test.cc
+++ b/compiler/optimizing/graph_test.cc
@@ -99,7 +99,7 @@
   ASSERT_NE(false_block, return_block);
 
   // Ensure the new block branches to the join block.
-  ASSERT_EQ(false_block->GetSuccessors().Get(0), return_block);
+  ASSERT_EQ(false_block->GetSuccessor(0), return_block);
 }
 
 // Test that the successors of an if block stay consistent after a SimplifyCFG.
@@ -134,7 +134,7 @@
   ASSERT_NE(true_block, return_block);
 
   // Ensure the new block branches to the join block.
-  ASSERT_EQ(true_block->GetSuccessors().Get(0), return_block);
+  ASSERT_EQ(true_block->GetSuccessor(0), return_block);
 }
 
 // Test that the successors of an if block stay consistent after a SimplifyCFG.
@@ -163,12 +163,12 @@
   ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(), return_block);
 
   // Ensure there is only one back edge.
-  ASSERT_EQ(if_block->GetPredecessors().Size(), 2u);
-  ASSERT_EQ(if_block->GetPredecessors().Get(0), entry_block);
-  ASSERT_NE(if_block->GetPredecessors().Get(1), if_block);
+  ASSERT_EQ(if_block->GetPredecessors().size(), 2u);
+  ASSERT_EQ(if_block->GetPredecessor(0), entry_block);
+  ASSERT_NE(if_block->GetPredecessor(1), if_block);
 
   // Ensure the new block is the back edge.
-  ASSERT_EQ(if_block->GetPredecessors().Get(1),
+  ASSERT_EQ(if_block->GetPredecessor(1),
             if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor());
 }
 
@@ -198,12 +198,12 @@
   ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(), return_block);
 
   // Ensure there is only one back edge.
-  ASSERT_EQ(if_block->GetPredecessors().Size(), 2u);
-  ASSERT_EQ(if_block->GetPredecessors().Get(0), entry_block);
-  ASSERT_NE(if_block->GetPredecessors().Get(1), if_block);
+  ASSERT_EQ(if_block->GetPredecessors().size(), 2u);
+  ASSERT_EQ(if_block->GetPredecessor(0), entry_block);
+  ASSERT_NE(if_block->GetPredecessor(1), if_block);
 
   // Ensure the new block is the back edge.
-  ASSERT_EQ(if_block->GetPredecessors().Get(1),
+  ASSERT_EQ(if_block->GetPredecessor(1),
             if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor());
 }
 
@@ -238,11 +238,11 @@
   ASSERT_EQ(if_instr->IfFalseSuccessor(), return_block);
 
   // Ensure there is only one pre header..
-  ASSERT_EQ(loop_block->GetPredecessors().Size(), 2u);
+  ASSERT_EQ(loop_block->GetPredecessors().size(), 2u);
 
   // Ensure the new block is the successor of the true block.
-  ASSERT_EQ(if_instr->IfTrueSuccessor()->GetSuccessors().Size(), 1u);
-  ASSERT_EQ(if_instr->IfTrueSuccessor()->GetSuccessors().Get(0),
+  ASSERT_EQ(if_instr->IfTrueSuccessor()->GetSuccessors().size(), 1u);
+  ASSERT_EQ(if_instr->IfTrueSuccessor()->GetSuccessor(0),
             loop_block->GetLoopInformation()->GetPreHeader());
 }
 
@@ -276,11 +276,11 @@
   ASSERT_EQ(if_instr->IfTrueSuccessor(), return_block);
 
   // Ensure there is only one pre header..
-  ASSERT_EQ(loop_block->GetPredecessors().Size(), 2u);
+  ASSERT_EQ(loop_block->GetPredecessors().size(), 2u);
 
   // Ensure the new block is the successor of the false block.
-  ASSERT_EQ(if_instr->IfFalseSuccessor()->GetSuccessors().Size(), 1u);
-  ASSERT_EQ(if_instr->IfFalseSuccessor()->GetSuccessors().Get(0),
+  ASSERT_EQ(if_instr->IfFalseSuccessor()->GetSuccessors().size(), 1u);
+  ASSERT_EQ(if_instr->IfFalseSuccessor()->GetSuccessor(0),
             loop_block->GetLoopInformation()->GetPreHeader());
 }
 
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 069a7a4..5b8e386 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -240,8 +240,7 @@
   void PrintPredecessors(HBasicBlock* block) {
     AddIndent();
     output_ << "predecessors";
-    for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
-      HBasicBlock* predecessor = block->GetPredecessors().Get(i);
+    for (HBasicBlock* predecessor : block->GetPredecessors()) {
       output_ << " \"B" << predecessor->GetBlockId() << "\" ";
     }
     if (block->IsEntryBlock() && (disasm_info_ != nullptr)) {
@@ -254,7 +253,7 @@
     AddIndent();
     output_ << "successors";
     for (size_t i = 0; i < block->NumberOfNormalSuccessors(); ++i) {
-      HBasicBlock* successor = block->GetSuccessors().Get(i);
+      HBasicBlock* successor = block->GetSuccessor(i);
       output_ << " \"B" << successor->GetBlockId() << "\" ";
     }
     output_<< std::endl;
@@ -263,8 +262,8 @@
   void PrintExceptionHandlers(HBasicBlock* block) {
     AddIndent();
     output_ << "xhandlers";
-    for (size_t i = block->NumberOfNormalSuccessors(); i < block->GetSuccessors().Size(); ++i) {
-      HBasicBlock* handler = block->GetSuccessors().Get(i);
+    for (size_t i = block->NumberOfNormalSuccessors(); i < block->GetSuccessors().size(); ++i) {
+      HBasicBlock* handler = block->GetSuccessor(i);
       output_ << " \"B" << handler->GetBlockId() << "\" ";
     }
     if (block->IsExitBlock() &&
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index 833dfb0..5bb4e8e 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -340,8 +340,8 @@
 
 void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
   ValueSet* set = nullptr;
-  const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
-  if (predecessors.Size() == 0 || predecessors.Get(0)->IsEntryBlock()) {
+  const ArenaVector<HBasicBlock*>& predecessors = block->GetPredecessors();
+  if (predecessors.size() == 0 || predecessors[0]->IsEntryBlock()) {
     // The entry block should only accumulate constant instructions, and
     // the builder puts constants only in the entry block.
     // Therefore, there is no need to propagate the value set to the next block.
@@ -349,8 +349,8 @@
   } else {
     HBasicBlock* dominator = block->GetDominator();
     ValueSet* dominator_set = sets_.Get(dominator->GetBlockId());
-    if (dominator->GetSuccessors().Size() == 1) {
-      DCHECK_EQ(dominator->GetSuccessors().Get(0), block);
+    if (dominator->GetSuccessors().size() == 1) {
+      DCHECK_EQ(dominator->GetSuccessor(0), block);
       set = dominator_set;
     } else {
       // We have to copy if the dominator has other successors, or `block` is not a successor
@@ -361,9 +361,9 @@
       if (block->IsLoopHeader()) {
         DCHECK_EQ(block->GetDominator(), block->GetLoopInformation()->GetPreHeader());
         set->Kill(side_effects_.GetLoopEffects(block));
-      } else if (predecessors.Size() > 1) {
-        for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
-          set->IntersectWith(sets_.Get(predecessors.Get(i)->GetBlockId()));
+      } else if (predecessors.size() > 1) {
+        for (HBasicBlock* predecessor : predecessors) {
+          set->IntersectWith(sets_.Get(predecessor->GetBlockId()));
           if (set->IsEmpty()) {
             break;
           }
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 0547ce8..efd4fcf 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -423,8 +423,8 @@
   }
 
   bool has_throw_predecessor = false;
-  for (size_t i = 0, e = exit_block->GetPredecessors().Size(); i < e; ++i) {
-    if (exit_block->GetPredecessors().Get(i)->GetLastInstruction()->IsThrow()) {
+  for (HBasicBlock* predecessor : exit_block->GetPredecessors()) {
+    if (predecessor->GetLastInstruction()->IsThrow()) {
       has_throw_predecessor = true;
       break;
     }
@@ -506,7 +506,7 @@
       ReferenceTypeInfo::TypeHandle return_handle =
         handles_->NewHandle(resolved_method->GetReturnType(true /* resolve */, pointer_size));
       return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
-         return_handle, return_handle->IsFinal() /* is_exact */));
+         return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */));
     }
   }
 
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index bc4a663..ec4a9ec 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -63,8 +63,8 @@
     // Provide boiler-plate instructions.
     parameter_ = new (&allocator_) HParameterValue(0, Primitive::kPrimNot);
     entry_->AddInstruction(parameter_);
-    constant_ = new (&allocator_) HConstant(Primitive::kPrimInt);
-    loop_preheader_->AddInstruction(constant_);
+    constant_ = graph_->GetIntConstant(42);
+    loop_preheader_->AddInstruction(new (&allocator_) HGoto());
     loop_header_->AddInstruction(new (&allocator_) HIf(parameter_));
     loop_body_->AddInstruction(new (&allocator_) HGoto());
     exit_->AddInstruction(new (&allocator_) HExit());
@@ -99,23 +99,6 @@
 // The actual LICM tests.
 //
 
-TEST_F(LICMTest, ConstantHoisting) {
-  BuildLoop();
-
-  // Populate the loop with instructions: set array to constant.
-  HInstruction* constant = new (&allocator_) HConstant(Primitive::kPrimDouble);
-  loop_body_->InsertInstructionBefore(constant, loop_body_->GetLastInstruction());
-  HInstruction* set_array = new (&allocator_) HArraySet(
-      parameter_, constant_, constant, Primitive::kPrimDouble, 0);
-  loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
-
-  EXPECT_EQ(constant->GetBlock(), loop_body_);
-  EXPECT_EQ(set_array->GetBlock(), loop_body_);
-  PerformLICM();
-  EXPECT_EQ(constant->GetBlock(), loop_preheader_);
-  EXPECT_EQ(set_array->GetBlock(), loop_body_);
-}
-
 TEST_F(LICMTest, FieldHoisting) {
   BuildLoop();
 
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 650c8e5..cc12a10 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -20,6 +20,7 @@
 #include "ssa_builder.h"
 #include "base/bit_vector-inl.h"
 #include "base/bit_utils.h"
+#include "mirror/class-inl.h"
 #include "utils/growable_array.h"
 #include "scoped_thread_state_change.h"
 
@@ -68,8 +69,8 @@
     if (!visited.IsBitSet(i)) {
       HBasicBlock* block = blocks_.Get(i);
       // We only need to update the successor, which might be live.
-      for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
-        block->GetSuccessors().Get(j)->RemovePredecessor(block);
+      for (HBasicBlock* successor : block->GetSuccessors()) {
+        successor->RemovePredecessor(block);
       }
       // Remove the block from the list of blocks, so that further analyses
       // never see it.
@@ -86,8 +87,7 @@
 
   visited->SetBit(id);
   visiting->SetBit(id);
-  for (size_t i = 0; i < block->GetSuccessors().Size(); i++) {
-    HBasicBlock* successor = block->GetSuccessors().Get(i);
+  for (HBasicBlock* successor : block->GetSuccessors()) {
     if (visiting->IsBitSet(successor->GetBlockId())) {
       successor->AddBackEdge(block);
     } else {
@@ -134,7 +134,7 @@
 }
 
 void HBasicBlock::ClearDominanceInformation() {
-  dominated_blocks_.Reset();
+  dominated_blocks_.clear();
   dominator_ = nullptr;
 }
 
@@ -143,8 +143,8 @@
   GrowableArray<size_t> visits(arena_, blocks_.Size());
   visits.SetSize(blocks_.Size());
   reverse_post_order_.Add(entry_block_);
-  for (size_t i = 0; i < entry_block_->GetSuccessors().Size(); i++) {
-    VisitBlockForDominatorTree(entry_block_->GetSuccessors().Get(i), entry_block_, &visits);
+  for (HBasicBlock* successor : entry_block_->GetSuccessors()) {
+    VisitBlockForDominatorTree(successor, entry_block_, &visits);
   }
 }
 
@@ -179,11 +179,11 @@
   // Once all the forward edges have been visited, we know the immediate
   // dominator of the block. We can then start visiting its successors.
   if (visits->Get(block->GetBlockId()) ==
-      block->GetPredecessors().Size() - block->NumberOfBackEdges()) {
+      block->GetPredecessors().size() - block->NumberOfBackEdges()) {
     block->GetDominator()->AddDominatedBlock(block);
     reverse_post_order_.Add(block);
-    for (size_t i = 0; i < block->GetSuccessors().Size(); i++) {
-      VisitBlockForDominatorTree(block->GetSuccessors().Get(i), block, visits);
+    for (HBasicBlock* successor : block->GetSuccessors()) {
+      VisitBlockForDominatorTree(successor, block, visits);
     }
   }
 }
@@ -224,14 +224,14 @@
   // Make sure the loop has only one pre header. This simplifies SSA building by having
   // to just look at the pre header to know which locals are initialized at entry of the
   // loop.
-  size_t number_of_incomings = header->GetPredecessors().Size() - info->NumberOfBackEdges();
+  size_t number_of_incomings = header->GetPredecessors().size() - info->NumberOfBackEdges();
   if (number_of_incomings != 1) {
     HBasicBlock* pre_header = new (arena_) HBasicBlock(this, header->GetDexPc());
     AddBlock(pre_header);
     pre_header->AddInstruction(new (arena_) HGoto(header->GetDexPc()));
 
-    for (size_t pred = 0; pred < header->GetPredecessors().Size(); ++pred) {
-      HBasicBlock* predecessor = header->GetPredecessors().Get(pred);
+    for (size_t pred = 0; pred < header->GetPredecessors().size(); ++pred) {
+      HBasicBlock* predecessor = header->GetPredecessor(pred);
       if (!info->IsBackEdge(*predecessor)) {
         predecessor->ReplaceSuccessor(header, pre_header);
         pred--;
@@ -241,13 +241,13 @@
   }
 
   // Make sure the first predecessor of a loop header is the incoming block.
-  if (info->IsBackEdge(*header->GetPredecessors().Get(0))) {
-    HBasicBlock* to_swap = header->GetPredecessors().Get(0);
-    for (size_t pred = 1, e = header->GetPredecessors().Size(); pred < e; ++pred) {
-      HBasicBlock* predecessor = header->GetPredecessors().Get(pred);
+  if (info->IsBackEdge(*header->GetPredecessor(0))) {
+    HBasicBlock* to_swap = header->GetPredecessor(0);
+    for (size_t pred = 1, e = header->GetPredecessors().size(); pred < e; ++pred) {
+      HBasicBlock* predecessor = header->GetPredecessor(pred);
       if (!info->IsBackEdge(*predecessor)) {
-        header->predecessors_.Put(pred, to_swap);
-        header->predecessors_.Put(0, predecessor);
+        header->predecessors_[pred] = to_swap;
+        header->predecessors_[0] = predecessor;
         break;
       }
     }
@@ -267,7 +267,7 @@
 }
 
 static bool CheckIfPredecessorAtIsExceptional(const HBasicBlock& block, size_t pred_idx) {
-  HBasicBlock* predecessor = block.GetPredecessors().Get(pred_idx);
+  HBasicBlock* predecessor = block.GetPredecessor(pred_idx);
   if (!predecessor->EndsWithTryBoundary()) {
     // Only edges from HTryBoundary can be exceptional.
     return false;
@@ -296,7 +296,7 @@
     }
 
     bool exceptional_predecessors_only = true;
-    for (size_t j = 0; j < catch_block->GetPredecessors().Size(); ++j) {
+    for (size_t j = 0; j < catch_block->GetPredecessors().size(); ++j) {
       if (!CheckIfPredecessorAtIsExceptional(*catch_block, j)) {
         exceptional_predecessors_only = false;
         break;
@@ -313,9 +313,9 @@
       // a MOVE_EXCEPTION instruction, as guaranteed by the verifier.
       DCHECK(!catch_block->GetFirstInstruction()->IsLoadException());
       HBasicBlock* normal_block = catch_block->SplitBefore(catch_block->GetFirstInstruction());
-      for (size_t j = 0; j < catch_block->GetPredecessors().Size(); ++j) {
+      for (size_t j = 0; j < catch_block->GetPredecessors().size(); ++j) {
         if (!CheckIfPredecessorAtIsExceptional(*catch_block, j)) {
-          catch_block->GetPredecessors().Get(j)->ReplaceSuccessor(catch_block, normal_block);
+          catch_block->GetPredecessor(j)->ReplaceSuccessor(catch_block, normal_block);
           --j;
         }
       }
@@ -337,7 +337,7 @@
     // Infer try membership from the first predecessor. Having simplified loops,
     // the first predecessor can never be a back edge and therefore it must have
     // been visited already and had its try membership set.
-    HBasicBlock* first_predecessor = block->GetPredecessors().Get(0);
+    HBasicBlock* first_predecessor = block->GetPredecessor(0);
     DCHECK(!block->IsLoopHeader() || !block->GetLoopInformation()->IsBackEdge(*first_predecessor));
     const HTryBoundary* try_entry = first_predecessor->ComputeTryEntryOfSuccessors();
     if (try_entry != nullptr) {
@@ -346,16 +346,6 @@
   }
 }
 
-bool HGraph::HasTryCatch() const {
-  for (size_t i = 0, e = blocks_.Size(); i < e; ++i) {
-    HBasicBlock* block = blocks_.Get(i);
-    if (block != nullptr && (block->IsTryBlock() || block->IsCatchBlock())) {
-      return true;
-    }
-  }
-  return false;
-}
-
 void HGraph::SimplifyCFG() {
   // Simplify the CFG for future analysis, and code generation:
   // (1): Split critical edges.
@@ -364,10 +354,10 @@
     HBasicBlock* block = blocks_.Get(i);
     if (block == nullptr) continue;
     if (block->NumberOfNormalSuccessors() > 1) {
-      for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
-        HBasicBlock* successor = block->GetSuccessors().Get(j);
+      for (size_t j = 0; j < block->GetSuccessors().size(); ++j) {
+        HBasicBlock* successor = block->GetSuccessor(j);
         DCHECK(!successor->IsCatchBlock());
-        if (successor->GetPredecessors().Size() > 1) {
+        if (successor->GetPredecessors().size() > 1) {
           SplitCriticalEdge(block, successor);
           --j;
         }
@@ -486,8 +476,8 @@
 
   blocks_.SetBit(block->GetBlockId());
   block->SetInLoop(this);
-  for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
-    PopulateRecursive(block->GetPredecessors().Get(i));
+  for (HBasicBlock* predecessor : block->GetPredecessors()) {
+    PopulateRecursive(predecessor);
   }
 }
 
@@ -1138,12 +1128,11 @@
   new_block->instructions_.SetBlockOfInstructions(new_block);
   AddInstruction(new (GetGraph()->GetArena()) HGoto(new_block->GetDexPc()));
 
-  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);
+  for (HBasicBlock* successor : GetSuccessors()) {
+    new_block->successors_.push_back(successor);
+    successor->predecessors_[successor->GetPredecessorIndexOf(this)] = new_block;
   }
-  successors_.Reset();
+  successors_.clear();
   AddSuccessor(new_block);
 
   GetGraph()->AddBlock(new_block);
@@ -1163,19 +1152,17 @@
   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);
+  for (HBasicBlock* successor : GetSuccessors()) {
+    new_block->successors_.push_back(successor);
+    successor->predecessors_[successor->GetPredecessorIndexOf(this)] = new_block;
   }
-  successors_.Reset();
+  successors_.clear();
 
-  for (size_t i = 0, e = GetDominatedBlocks().Size(); i < e; ++i) {
-    HBasicBlock* dominated = GetDominatedBlocks().Get(i);
+  for (HBasicBlock* dominated : GetDominatedBlocks()) {
     dominated->dominator_ = new_block;
-    new_block->dominated_blocks_.Add(dominated);
+    new_block->dominated_blocks_.push_back(dominated);
   }
-  dominated_blocks_.Reset();
+  dominated_blocks_.clear();
   return new_block;
 }
 
@@ -1228,7 +1215,7 @@
 }
 
 bool HTryBoundary::HasSameExceptionHandlersAs(const HTryBoundary& other) const {
-  if (GetBlock()->GetSuccessors().Size() != other.GetBlock()->GetSuccessors().Size()) {
+  if (GetBlock()->GetSuccessors().size() != other.GetBlock()->GetSuccessors().size()) {
     return false;
   }
 
@@ -1288,7 +1275,7 @@
   // Dominators must be removed after all the blocks they dominate. This way
   // a loop header is removed last, a requirement for correct loop information
   // iteration.
-  DCHECK(dominated_blocks_.IsEmpty());
+  DCHECK(dominated_blocks_.empty());
 
   // Remove the block from all loops it is included in.
   for (HLoopInformationOutwardIterator it(*this); !it.Done(); it.Advance()) {
@@ -1304,36 +1291,34 @@
 
   // Disconnect the block from its predecessors and update their control-flow
   // instructions.
-  for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) {
-    HBasicBlock* predecessor = predecessors_.Get(i);
+  for (HBasicBlock* predecessor : predecessors_) {
     HInstruction* last_instruction = predecessor->GetLastInstruction();
     predecessor->RemoveInstruction(last_instruction);
     predecessor->RemoveSuccessor(this);
-    if (predecessor->GetSuccessors().Size() == 1u) {
+    if (predecessor->GetSuccessors().size() == 1u) {
       DCHECK(last_instruction->IsIf());
       predecessor->AddInstruction(new (graph_->GetArena()) HGoto(last_instruction->GetDexPc()));
     } else {
       // The predecessor has no remaining successors and therefore must be dead.
       // We deliberately leave it without a control-flow instruction so that the
       // SSAChecker fails unless it is not removed during the pass too.
-      DCHECK_EQ(predecessor->GetSuccessors().Size(), 0u);
+      DCHECK_EQ(predecessor->GetSuccessors().size(), 0u);
     }
   }
-  predecessors_.Reset();
+  predecessors_.clear();
 
   // Disconnect the block from its successors and update their phis.
-  for (size_t i = 0, e = successors_.Size(); i < e; ++i) {
-    HBasicBlock* successor = successors_.Get(i);
+  for (HBasicBlock* successor : successors_) {
     // Delete this block from the list of predecessors.
     size_t this_index = successor->GetPredecessorIndexOf(this);
-    successor->predecessors_.DeleteAt(this_index);
+    successor->predecessors_.erase(successor->predecessors_.begin() + this_index);
 
     // Check that `successor` has other predecessors, otherwise `this` is the
     // dominator of `successor` which violates the order DCHECKed at the top.
-    DCHECK(!successor->predecessors_.IsEmpty());
+    DCHECK(!successor->predecessors_.empty());
 
     // Remove this block's entries in the successor's phis.
-    if (successor->predecessors_.Size() == 1u) {
+    if (successor->predecessors_.size() == 1u) {
       // The successor has just one predecessor left. Replace phis with the only
       // remaining input.
       for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
@@ -1347,7 +1332,7 @@
       }
     }
   }
-  successors_.Reset();
+  successors_.clear();
 
   // Disconnect from the dominator.
   dominator_->RemoveDominatedBlock(this);
@@ -1361,11 +1346,9 @@
 
 void HBasicBlock::MergeWith(HBasicBlock* other) {
   DCHECK_EQ(GetGraph(), other->GetGraph());
-  DCHECK(GetDominatedBlocks().Contains(other));
-  DCHECK_EQ(GetSuccessors().Size(), 1u);
-  DCHECK_EQ(GetSuccessors().Get(0), other);
-  DCHECK_EQ(other->GetPredecessors().Size(), 1u);
-  DCHECK_EQ(other->GetPredecessors().Get(0), this);
+  DCHECK(ContainsElement(dominated_blocks_, other));
+  DCHECK_EQ(GetSingleSuccessor(), other);
+  DCHECK_EQ(other->GetSinglePredecessor(), this);
   DCHECK(other->GetPhis().IsEmpty());
 
   // Move instructions from `other` to `this`.
@@ -1385,24 +1368,23 @@
   }
 
   // Update links to the successors of `other`.
-  successors_.Reset();
-  while (!other->successors_.IsEmpty()) {
-    HBasicBlock* successor = other->successors_.Get(0);
+  successors_.clear();
+  while (!other->successors_.empty()) {
+    HBasicBlock* successor = other->GetSuccessor(0);
     successor->ReplacePredecessor(other, this);
   }
 
   // Update the dominator tree.
-  dominated_blocks_.Delete(other);
-  for (size_t i = 0, e = other->GetDominatedBlocks().Size(); i < e; ++i) {
-    HBasicBlock* dominated = other->GetDominatedBlocks().Get(i);
-    dominated_blocks_.Add(dominated);
+  RemoveDominatedBlock(other);
+  for (HBasicBlock* dominated : other->GetDominatedBlocks()) {
+    dominated_blocks_.push_back(dominated);
     dominated->SetDominator(this);
   }
-  other->dominated_blocks_.Reset();
+  other->dominated_blocks_.clear();
   other->dominator_ = nullptr;
 
   // Clear the list of predecessors of `other` in preparation of deleting it.
-  other->predecessors_.Reset();
+  other->predecessors_.clear();
 
   // Delete `other` from the graph. The function updates reverse post order.
   graph_->DeleteDeadBlock(other);
@@ -1411,11 +1393,10 @@
 
 void HBasicBlock::MergeWithInlined(HBasicBlock* other) {
   DCHECK_NE(GetGraph(), other->GetGraph());
-  DCHECK(GetDominatedBlocks().IsEmpty());
-  DCHECK(GetSuccessors().IsEmpty());
+  DCHECK(GetDominatedBlocks().empty());
+  DCHECK(GetSuccessors().empty());
   DCHECK(!EndsWithControlFlowInstruction());
-  DCHECK_EQ(other->GetPredecessors().Size(), 1u);
-  DCHECK(other->GetPredecessors().Get(0)->IsEntryBlock());
+  DCHECK(other->GetSinglePredecessor()->IsEntryBlock());
   DCHECK(other->GetPhis().IsEmpty());
   DCHECK(!other->IsInLoop());
 
@@ -1424,34 +1405,33 @@
   other->instructions_.SetBlockOfInstructions(this);
 
   // Update links to the successors of `other`.
-  successors_.Reset();
-  while (!other->successors_.IsEmpty()) {
-    HBasicBlock* successor = other->successors_.Get(0);
+  successors_.clear();
+  while (!other->successors_.empty()) {
+    HBasicBlock* successor = other->GetSuccessor(0);
     successor->ReplacePredecessor(other, this);
   }
 
   // Update the dominator tree.
-  for (size_t i = 0, e = other->GetDominatedBlocks().Size(); i < e; ++i) {
-    HBasicBlock* dominated = other->GetDominatedBlocks().Get(i);
-    dominated_blocks_.Add(dominated);
+  for (HBasicBlock* dominated : other->GetDominatedBlocks()) {
+    dominated_blocks_.push_back(dominated);
     dominated->SetDominator(this);
   }
-  other->dominated_blocks_.Reset();
+  other->dominated_blocks_.clear();
   other->dominator_ = nullptr;
   other->graph_ = nullptr;
 }
 
 void HBasicBlock::ReplaceWith(HBasicBlock* other) {
-  while (!GetPredecessors().IsEmpty()) {
-    HBasicBlock* predecessor = GetPredecessors().Get(0);
+  while (!GetPredecessors().empty()) {
+    HBasicBlock* predecessor = GetPredecessor(0);
     predecessor->ReplaceSuccessor(this, other);
   }
-  while (!GetSuccessors().IsEmpty()) {
-    HBasicBlock* successor = GetSuccessors().Get(0);
+  while (!GetSuccessors().empty()) {
+    HBasicBlock* successor = GetSuccessor(0);
     successor->ReplacePredecessor(this, other);
   }
-  for (size_t i = 0; i < dominated_blocks_.Size(); ++i) {
-    other->AddDominatedBlock(dominated_blocks_.Get(i));
+  for (HBasicBlock* dominated : GetDominatedBlocks()) {
+    other->AddDominatedBlock(dominated);
   }
   GetDominator()->ReplaceDominatedBlock(this, other);
   other->SetDominator(GetDominator());
@@ -1474,9 +1454,9 @@
 
 void HGraph::DeleteDeadBlock(HBasicBlock* block) {
   DCHECK_EQ(block->GetGraph(), this);
-  DCHECK(block->GetSuccessors().IsEmpty());
-  DCHECK(block->GetPredecessors().IsEmpty());
-  DCHECK(block->GetDominatedBlocks().IsEmpty());
+  DCHECK(block->GetSuccessors().empty());
+  DCHECK(block->GetPredecessors().empty());
+  DCHECK(block->GetDominatedBlocks().empty());
   DCHECK(block->GetDominator() == nullptr);
 
   for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
@@ -1550,16 +1530,16 @@
     HBasicBlock* at = invoke->GetBlock();
     HBasicBlock* to = at->SplitAfter(invoke);
 
-    HBasicBlock* first = entry_block_->GetSuccessors().Get(0);
+    HBasicBlock* first = entry_block_->GetSuccessor(0);
     DCHECK(!first->IsInLoop());
     at->MergeWithInlined(first);
     exit_block_->ReplaceWith(to);
 
     // Update all predecessors of the exit block (now the `to` block)
     // to not `HReturn` but `HGoto` instead.
-    bool returns_void = to->GetPredecessors().Get(0)->GetLastInstruction()->IsReturnVoid();
-    if (to->GetPredecessors().Size() == 1) {
-      HBasicBlock* predecessor = to->GetPredecessors().Get(0);
+    bool returns_void = to->GetPredecessor(0)->GetLastInstruction()->IsReturnVoid();
+    if (to->GetPredecessors().size() == 1) {
+      HBasicBlock* predecessor = to->GetPredecessor(0);
       HInstruction* last = predecessor->GetLastInstruction();
       if (!returns_void) {
         return_value = last->InputAt(0);
@@ -1573,8 +1553,7 @@
             allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc());
         to->AddPhi(return_value->AsPhi());
       }
-      for (size_t i = 0, e = to->GetPredecessors().Size(); i < e; ++i) {
-        HBasicBlock* predecessor = to->GetPredecessors().Get(i);
+      for (HBasicBlock* predecessor : to->GetPredecessors()) {
         HInstruction* last = predecessor->GetLastInstruction();
         if (!returns_void) {
           return_value->AsPhi()->AddInput(last->InputAt(0));
@@ -1726,8 +1705,8 @@
   AddBlock(new_pre_header);
 
   header->ReplacePredecessor(pre_header, new_pre_header);
-  pre_header->successors_.Reset();
-  pre_header->dominated_blocks_.Reset();
+  pre_header->successors_.clear();
+  pre_header->dominated_blocks_.clear();
 
   pre_header->AddSuccessor(if_block);
   if_block->AddSuccessor(dummy_block);  // True successor
@@ -1735,15 +1714,15 @@
   dummy_block->AddSuccessor(new_pre_header);
   deopt_block->AddSuccessor(new_pre_header);
 
-  pre_header->dominated_blocks_.Add(if_block);
+  pre_header->dominated_blocks_.push_back(if_block);
   if_block->SetDominator(pre_header);
-  if_block->dominated_blocks_.Add(dummy_block);
+  if_block->dominated_blocks_.push_back(dummy_block);
   dummy_block->SetDominator(if_block);
-  if_block->dominated_blocks_.Add(deopt_block);
+  if_block->dominated_blocks_.push_back(deopt_block);
   deopt_block->SetDominator(if_block);
-  if_block->dominated_blocks_.Add(new_pre_header);
+  if_block->dominated_blocks_.push_back(new_pre_header);
   new_pre_header->SetDominator(if_block);
-  new_pre_header->dominated_blocks_.Add(header);
+  new_pre_header->dominated_blocks_.push_back(header);
   header->SetDominator(new_pre_header);
 
   size_t index_of_header = 0;
@@ -1785,7 +1764,7 @@
       DCHECK(upper_bound_rti.IsSupertypeOf(rti))
           << " upper_bound_rti: " << upper_bound_rti
           << " rti: " << rti;
-      DCHECK(!upper_bound_rti.GetTypeHandle()->IsFinal() || rti.IsExact());
+      DCHECK(!upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes() || rti.IsExact());
     }
   }
   reference_type_info_ = rti;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 23d605b..d52a4f7 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -17,11 +17,13 @@
 #ifndef ART_COMPILER_OPTIMIZING_NODES_H_
 #define ART_COMPILER_OPTIMIZING_NODES_H_
 
+#include <algorithm>
 #include <array>
 #include <type_traits>
 
 #include "base/arena_containers.h"
 #include "base/arena_object.h"
+#include "base/stl_util.h"
 #include "dex/compiler_enums.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "handle.h"
@@ -155,6 +157,7 @@
         number_of_in_vregs_(0),
         temporaries_vreg_slots_(0),
         has_bounds_checks_(false),
+        has_try_catch_(false),
         debuggable_(debuggable),
         current_instruction_id_(start_instruction_id),
         dex_file_(dex_file),
@@ -280,7 +283,6 @@
   }
 
   uint16_t GetNumberOfVRegs() const {
-    DCHECK(!in_ssa_form_);
     return number_of_vregs_;
   }
 
@@ -358,8 +360,8 @@
     return instruction_set_;
   }
 
-  // TODO: Remove once the full compilation pipeline is enabled for try/catch.
-  bool HasTryCatch() const;
+  bool HasTryCatch() const { return has_try_catch_; }
+  void SetHasTryCatch(bool value) { has_try_catch_ = value; }
 
  private:
   void VisitBlockForDominatorTree(HBasicBlock* block,
@@ -431,6 +433,10 @@
   // Has bounds checks. We can totally skip BCE if it's false.
   bool has_bounds_checks_;
 
+  // Flag whether there are any try/catch blocks in the graph. We will skip
+  // try/catch-related passes if false.
+  bool has_try_catch_;
+
   // Indicates whether the graph should be compiled in a way that
   // ensures full debuggability. If false, we can apply more
   // aggressive optimizations that may limit the level of debugging.
@@ -630,26 +636,44 @@
  public:
   HBasicBlock(HGraph* graph, uint32_t dex_pc = kNoDexPc)
       : graph_(graph),
-        predecessors_(graph->GetArena(), kDefaultNumberOfPredecessors),
-        successors_(graph->GetArena(), kDefaultNumberOfSuccessors),
+        predecessors_(graph->GetArena()->Adapter(kArenaAllocPredecessors)),
+        successors_(graph->GetArena()->Adapter(kArenaAllocSuccessors)),
         loop_information_(nullptr),
         dominator_(nullptr),
-        dominated_blocks_(graph->GetArena(), kDefaultNumberOfDominatedBlocks),
+        dominated_blocks_(graph->GetArena()->Adapter(kArenaAllocDominated)),
         block_id_(-1),
         dex_pc_(dex_pc),
         lifetime_start_(kNoLifetime),
         lifetime_end_(kNoLifetime),
-        try_catch_information_(nullptr) {}
+        try_catch_information_(nullptr) {
+    predecessors_.reserve(kDefaultNumberOfPredecessors);
+    successors_.reserve(kDefaultNumberOfSuccessors);
+    dominated_blocks_.reserve(kDefaultNumberOfDominatedBlocks);
+  }
 
-  const GrowableArray<HBasicBlock*>& GetPredecessors() const {
+  const ArenaVector<HBasicBlock*>& GetPredecessors() const {
     return predecessors_;
   }
 
-  const GrowableArray<HBasicBlock*>& GetSuccessors() const {
+  HBasicBlock* GetPredecessor(size_t pred_idx) const {
+    DCHECK_LT(pred_idx, predecessors_.size());
+    return predecessors_[pred_idx];
+  }
+
+  const ArenaVector<HBasicBlock*>& GetSuccessors() const {
     return successors_;
   }
 
-  const GrowableArray<HBasicBlock*>& GetDominatedBlocks() const {
+  HBasicBlock* GetSuccessor(size_t succ_idx) const {
+    DCHECK_LT(succ_idx, successors_.size());
+    return successors_[succ_idx];
+  }
+
+  bool HasSuccessor(const HBasicBlock* block, size_t start_from = 0u) {
+    return ContainsElement(successors_, block, start_from);
+  }
+
+  const ArenaVector<HBasicBlock*>& GetDominatedBlocks() const {
     return dominated_blocks_;
   }
 
@@ -689,18 +713,16 @@
 
   HBasicBlock* GetDominator() const { return dominator_; }
   void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; }
-  void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.Add(block); }
-  void RemoveDominatedBlock(HBasicBlock* block) { dominated_blocks_.Delete(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();
+  void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.push_back(block); }
+
+  void RemoveDominatedBlock(HBasicBlock* block) {
+    RemoveElement(dominated_blocks_, block);
   }
+
+  void ReplaceDominatedBlock(HBasicBlock* existing, HBasicBlock* new_block) {
+    ReplaceElement(dominated_blocks_, existing, new_block);
+  }
+
   void ClearDominanceInformation();
 
   int NumberOfBackEdges() const {
@@ -715,24 +737,22 @@
   const HInstructionList& GetPhis() const { return phis_; }
 
   void AddSuccessor(HBasicBlock* block) {
-    successors_.Add(block);
-    block->predecessors_.Add(this);
+    successors_.push_back(block);
+    block->predecessors_.push_back(this);
   }
 
   void ReplaceSuccessor(HBasicBlock* existing, HBasicBlock* new_block) {
     size_t successor_index = GetSuccessorIndexOf(existing);
-    DCHECK_NE(successor_index, static_cast<size_t>(-1));
     existing->RemovePredecessor(this);
-    new_block->predecessors_.Add(this);
-    successors_.Put(successor_index, new_block);
+    new_block->predecessors_.push_back(this);
+    successors_[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);
+    new_block->successors_.push_back(this);
+    predecessors_[predecessor_index] = new_block;
   }
 
   // Insert `this` between `predecessor` and `successor. This method
@@ -740,85 +760,69 @@
   // `predecessor` and `successor`.
   void InsertBetween(HBasicBlock* predecessor, HBasicBlock* successor) {
     size_t predecessor_index = successor->GetPredecessorIndexOf(predecessor);
-    DCHECK_NE(predecessor_index, static_cast<size_t>(-1));
     size_t successor_index = predecessor->GetSuccessorIndexOf(successor);
-    DCHECK_NE(successor_index, static_cast<size_t>(-1));
-    successor->predecessors_.Put(predecessor_index, this);
-    predecessor->successors_.Put(successor_index, this);
-    successors_.Add(successor);
-    predecessors_.Add(predecessor);
+    successor->predecessors_[predecessor_index] = this;
+    predecessor->successors_[successor_index] = this;
+    successors_.push_back(successor);
+    predecessors_.push_back(predecessor);
   }
 
   void RemovePredecessor(HBasicBlock* block) {
-    predecessors_.Delete(block);
+    predecessors_.erase(predecessors_.begin() + GetPredecessorIndexOf(block));
   }
 
   void RemoveSuccessor(HBasicBlock* block) {
-    successors_.Delete(block);
+    successors_.erase(successors_.begin() + GetSuccessorIndexOf(block));
   }
 
   void ClearAllPredecessors() {
-    predecessors_.Reset();
+    predecessors_.clear();
   }
 
   void AddPredecessor(HBasicBlock* block) {
-    predecessors_.Add(block);
-    block->successors_.Add(this);
+    predecessors_.push_back(block);
+    block->successors_.push_back(this);
   }
 
   void SwapPredecessors() {
-    DCHECK_EQ(predecessors_.Size(), 2u);
-    HBasicBlock* temp = predecessors_.Get(0);
-    predecessors_.Put(0, predecessors_.Get(1));
-    predecessors_.Put(1, temp);
+    DCHECK_EQ(predecessors_.size(), 2u);
+    std::swap(predecessors_[0], predecessors_[1]);
   }
 
   void SwapSuccessors() {
-    DCHECK_EQ(successors_.Size(), 2u);
-    HBasicBlock* temp = successors_.Get(0);
-    successors_.Put(0, successors_.Get(1));
-    successors_.Put(1, temp);
+    DCHECK_EQ(successors_.size(), 2u);
+    std::swap(successors_[0], successors_[1]);
   }
 
   size_t GetPredecessorIndexOf(HBasicBlock* predecessor) const {
-    for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) {
-      if (predecessors_.Get(i) == predecessor) {
-        return i;
-      }
-    }
-    return -1;
+    return IndexOfElement(predecessors_, predecessor);
   }
 
   size_t GetSuccessorIndexOf(HBasicBlock* successor) const {
-    for (size_t i = 0, e = successors_.Size(); i < e; ++i) {
-      if (successors_.Get(i) == successor) {
-        return i;
-      }
-    }
-    return -1;
+    return IndexOfElement(successors_, successor);
   }
 
   HBasicBlock* GetSinglePredecessor() const {
-    DCHECK_EQ(GetPredecessors().Size(), 1u);
-    return GetPredecessors().Get(0);
+    DCHECK_EQ(GetPredecessors().size(), 1u);
+    return GetPredecessor(0);
   }
 
   HBasicBlock* GetSingleSuccessor() const {
-    DCHECK_EQ(GetSuccessors().Size(), 1u);
-    return GetSuccessors().Get(0);
+    DCHECK_EQ(GetSuccessors().size(), 1u);
+    return GetSuccessor(0);
   }
 
   // Returns whether the first occurrence of `predecessor` in the list of
   // predecessors is at index `idx`.
   bool IsFirstIndexOfPredecessor(HBasicBlock* predecessor, size_t idx) const {
-    DCHECK_EQ(GetPredecessors().Get(idx), predecessor);
+    DCHECK_EQ(GetPredecessor(idx), predecessor);
     return GetPredecessorIndexOf(predecessor) == idx;
   }
 
   // Returns the number of non-exceptional successors. SsaChecker ensures that
   // these are stored at the beginning of the successor list.
   size_t NumberOfNormalSuccessors() const {
-    return EndsWithTryBoundary() ? 1 : GetSuccessors().Size();
+    return EndsWithTryBoundary() ? 1 : GetSuccessors().size();
   }
 
   // Split the block into two blocks just before `cursor`. Returns the newly
@@ -883,8 +887,7 @@
 
   bool IsLoopPreHeaderFirstPredecessor() const {
     DCHECK(IsLoopHeader());
-    DCHECK(!GetPredecessors().IsEmpty());
-    return GetPredecessors().Get(0) == GetLoopInformation()->GetPreHeader();
+    return GetPredecessor(0) == GetLoopInformation()->GetPreHeader();
   }
 
   HLoopInformation* GetLoopInformation() const {
@@ -954,13 +957,13 @@
 
  private:
   HGraph* graph_;
-  GrowableArray<HBasicBlock*> predecessors_;
-  GrowableArray<HBasicBlock*> successors_;
+  ArenaVector<HBasicBlock*> predecessors_;
+  ArenaVector<HBasicBlock*> successors_;
   HInstructionList instructions_;
   HInstructionList phis_;
   HLoopInformation* loop_information_;
   HBasicBlock* dominator_;
-  GrowableArray<HBasicBlock*> dominated_blocks_;
+  ArenaVector<HBasicBlock*> dominated_blocks_;
   int block_id_;
   // The dex program counter of the first instruction of this block.
   const uint32_t dex_pc_;
@@ -2188,6 +2191,8 @@
   virtual bool IsZero() const { return false; }
   virtual bool IsOne() const { return false; }
 
+  virtual uint64_t GetValueAsUint64() const = 0;
+
   DECLARE_INSTRUCTION(Constant);
 
  private:
@@ -2200,6 +2205,8 @@
     return true;
   }
 
+  uint64_t GetValueAsUint64() const OVERRIDE { return 0; }
+
   size_t ComputeHashCode() const OVERRIDE { return 0; }
 
   DECLARE_INSTRUCTION(NullConstant);
@@ -2217,6 +2224,8 @@
  public:
   int32_t GetValue() const { return value_; }
 
+  uint64_t GetValueAsUint64() const OVERRIDE { return static_cast<uint64_t>(value_); }
+
   bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
     DCHECK(other->IsIntConstant());
     return other->AsIntConstant()->value_ == value_;
@@ -2248,6 +2257,8 @@
  public:
   int64_t GetValue() const { return value_; }
 
+  uint64_t GetValueAsUint64() const OVERRIDE { return value_; }
+
   bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
     DCHECK(other->IsLongConstant());
     return other->AsLongConstant()->value_ == value_;
@@ -2283,11 +2294,11 @@
   bool IsControlFlow() const OVERRIDE { return true; }
 
   HBasicBlock* IfTrueSuccessor() const {
-    return GetBlock()->GetSuccessors().Get(0);
+    return GetBlock()->GetSuccessor(0);
   }
 
   HBasicBlock* IfFalseSuccessor() const {
-    return GetBlock()->GetSuccessors().Get(1);
+    return GetBlock()->GetSuccessor(1);
   }
 
   DECLARE_INSTRUCTION(If);
@@ -2315,14 +2326,13 @@
   bool IsControlFlow() const OVERRIDE { return true; }
 
   // Returns the block's non-exceptional successor (index zero).
-  HBasicBlock* GetNormalFlowSuccessor() const { return GetBlock()->GetSuccessors().Get(0); }
+  HBasicBlock* GetNormalFlowSuccessor() const { return GetBlock()->GetSuccessor(0); }
 
   // Returns whether `handler` is among its exception handlers (non-zero index
   // successors).
   bool HasExceptionHandler(const HBasicBlock& handler) const {
     DCHECK(handler.IsCatchBlock());
-    return GetBlock()->GetSuccessors().Contains(
-        const_cast<HBasicBlock*>(&handler), /* start_from */ 1);
+    return GetBlock()->HasSuccessor(&handler, 1u /* Skip first successor. */);
   }
 
   // If not present already, adds `handler` to its block's list of exception
@@ -2352,8 +2362,8 @@
   explicit HExceptionHandlerIterator(const HTryBoundary& try_boundary)
     : block_(*try_boundary.GetBlock()), index_(block_.NumberOfNormalSuccessors()) {}
 
-  bool Done() const { return index_ == block_.GetSuccessors().Size(); }
-  HBasicBlock* Current() const { return block_.GetSuccessors().Get(index_); }
+  bool Done() const { return index_ == block_.GetSuccessors().size(); }
+  HBasicBlock* Current() const { return block_.GetSuccessor(index_); }
   size_t CurrentSuccessorIndex() const { return index_; }
   void Advance() { ++index_; }
 
@@ -2868,10 +2878,13 @@
  public:
   float GetValue() const { return value_; }
 
+  uint64_t GetValueAsUint64() const OVERRIDE {
+    return static_cast<uint64_t>(bit_cast<uint32_t, float>(value_));
+  }
+
   bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
     DCHECK(other->IsFloatConstant());
-    return bit_cast<uint32_t, float>(other->AsFloatConstant()->value_) ==
-        bit_cast<uint32_t, float>(value_);
+    return other->AsFloatConstant()->GetValueAsUint64() == GetValueAsUint64();
   }
 
   size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
@@ -2909,10 +2922,11 @@
  public:
   double GetValue() const { return value_; }
 
+  uint64_t GetValueAsUint64() const OVERRIDE { return bit_cast<uint64_t, double>(value_); }
+
   bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
     DCHECK(other->IsDoubleConstant());
-    return bit_cast<uint64_t, double>(other->AsDoubleConstant()->value_) ==
-        bit_cast<uint64_t, double>(value_);
+    return other->AsDoubleConstant()->GetValueAsUint64() == GetValueAsUint64();
   }
 
   size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
@@ -4005,6 +4019,13 @@
   bool IsDead() const { return !is_live_; }
   bool IsLive() const { return is_live_; }
 
+  bool IsVRegEquivalentOf(HInstruction* other) const {
+    return other != nullptr
+        && other->IsPhi()
+        && other->AsPhi()->GetBlock() == GetBlock()
+        && other->AsPhi()->GetRegNumber() == GetRegNumber();
+  }
+
   // Returns the next equivalent phi (starting from the current one) or null if there is none.
   // An equivalent phi is a phi having the same dex register and type.
   // It assumes that phis with the same dex register are adjacent.
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index f549ba8..4421460 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -485,34 +485,43 @@
 
   RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer);
 
+  // TODO: Update passes incompatible with try/catch so we have the same
+  //       pipeline for all methods.
   if (graph->HasTryCatch()) {
-    // TODO: Update the optimizations below to work correctly under try/catch
-    //       semantics. The optimizations above suffice for running codegen
-    //       in the meanwhile.
-    return;
+    HOptimization* optimizations2[] = {
+      side_effects,
+      gvn,
+      dce2,
+      // The codegen has a few assumptions that only the instruction simplifier
+      // can satisfy. For example, the code generator does not expect to see a
+      // HTypeConversion from a type to the same type.
+      simplify4,
+    };
+
+    RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer);
+  } else {
+    MaybeRunInliner(graph, driver, stats, dex_compilation_unit, pass_observer, handles);
+
+    HOptimization* optimizations2[] = {
+      // BooleanSimplifier depends on the InstructionSimplifier removing
+      // redundant suspend checks to recognize empty blocks.
+      boolean_simplify,
+      fold2,  // TODO: if we don't inline we can also skip fold2.
+      side_effects,
+      gvn,
+      licm,
+      bce,
+      simplify3,
+      dce2,
+      // The codegen has a few assumptions that only the instruction simplifier
+      // can satisfy. For example, the code generator does not expect to see a
+      // HTypeConversion from a type to the same type.
+      simplify4,
+    };
+
+    RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer);
   }
 
-  MaybeRunInliner(graph, driver, stats, dex_compilation_unit, pass_observer, handles);
-
-  HOptimization* optimizations2[] = {
-    // BooleanSimplifier depends on the InstructionSimplifier removing redundant
-    // suspend checks to recognize empty blocks.
-    boolean_simplify,
-    fold2,  // TODO: if we don't inline we can also skip fold2.
-    side_effects,
-    gvn,
-    licm,
-    bce,
-    simplify3,
-    dce2,
-    // The codegen has a few assumptions that only the instruction simplifier can
-    // satisfy. For example, the code generator does not expect to see a
-    // HTypeConversion from a type to the same type.
-    simplify4,
-  };
-
-  RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer);
-
   RunArchOptimizations(driver->GetInstructionSet(), graph, stats, pass_observer);
 }
 
@@ -560,17 +569,18 @@
                                                      CompilerDriver* compiler_driver,
                                                      const DexCompilationUnit& dex_compilation_unit,
                                                      PassObserver* pass_observer) const {
+  if (graph->HasTryCatch() && graph->IsDebuggable()) {
+    // TODO: b/24054676, stop creating catch phis eagerly to avoid special cases like phis without
+    // inputs.
+    return nullptr;
+  }
+
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScopeCollection handles(soa.Self());
   soa.Self()->TransitionFromRunnableToSuspended(kNative);
   RunOptimizations(graph, compiler_driver, compilation_stats_.get(),
                    dex_compilation_unit, pass_observer, &handles);
 
-  if (graph->HasTryCatch()) {
-    soa.Self()->TransitionFromSuspendedToRunnable();
-    return nullptr;
-  }
-
   AllocateRegisters(graph, codegen, pass_observer);
 
   ArenaAllocator* arena = graph->GetArena();
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 934514e..34850a5 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -71,23 +71,23 @@
   void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
     PrintString("BasicBlock ");
     PrintInt(block->GetBlockId());
-    const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
-    if (!predecessors.IsEmpty()) {
+    const ArenaVector<HBasicBlock*>& predecessors = block->GetPredecessors();
+    if (!predecessors.empty()) {
       PrintString(", pred: ");
-      for (size_t i = 0; i < predecessors.Size() -1; i++) {
-        PrintInt(predecessors.Get(i)->GetBlockId());
+      for (size_t i = 0; i < predecessors.size() -1; i++) {
+        PrintInt(predecessors[i]->GetBlockId());
         PrintString(", ");
       }
-      PrintInt(predecessors.Peek()->GetBlockId());
+      PrintInt(predecessors.back()->GetBlockId());
     }
-    const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors();
-    if (!successors.IsEmpty()) {
+    const ArenaVector<HBasicBlock*>& successors = block->GetSuccessors();
+    if (!successors.empty()) {
       PrintString(", succ: ");
-      for (size_t i = 0; i < successors.Size() - 1; i++) {
-        PrintInt(successors.Get(i)->GetBlockId());
+      for (size_t i = 0; i < successors.size() - 1; i++) {
+        PrintInt(successors[i]->GetBlockId());
         PrintString(", ");
       }
-      PrintInt(successors.Peek()->GetBlockId());
+      PrintInt(successors.back()->GetBlockId());
     }
     PrintNewLine();
     HGraphVisitor::VisitBasicBlock(block);
@@ -131,7 +131,7 @@
     PrintString("  ");
     PrintInt(gota->GetId());
     PrintString(": Goto ");
-    PrintInt(current_block_->GetSuccessors().Get(0)->GetBlockId());
+    PrintInt(current_block_->GetSuccessor(0)->GetBlockId());
     PrintNewLine();
   }
 
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 0384e46..a88c543 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -167,7 +167,7 @@
   ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
   HBoundType* bound_type = new (arena) HBoundType(obj, class_rti, upper_can_be_null);
   // Narrow the type as much as possible.
-  if (class_rti.GetTypeHandle()->IsFinal()) {
+  if (class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) {
     bound_type->SetReferenceTypeInfo(
         ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ true));
   } else if (obj_rti.IsValid() && class_rti.IsSupertypeOf(obj_rti)) {
@@ -380,7 +380,7 @@
   } else if (klass != nullptr) {
     ScopedObjectAccess soa(Thread::Current());
     ReferenceTypeInfo::TypeHandle handle = handles_->NewHandle(klass);
-    is_exact = is_exact || klass->IsFinal();
+    is_exact = is_exact || klass->CannotBeAssignedFromOtherTypes();
     instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
   } else {
     instr->SetReferenceTypeInfo(
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 37c8bc5..a4f1f45 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -56,6 +56,7 @@
         long_spill_slots_(allocator, kDefaultNumberOfSpillSlots),
         float_spill_slots_(allocator, kDefaultNumberOfSpillSlots),
         double_spill_slots_(allocator, kDefaultNumberOfSpillSlots),
+        catch_phi_spill_slots_(0),
         safepoints_(allocator, 0),
         processing_core_registers_(false),
         number_of_registers_(-1),
@@ -124,9 +125,7 @@
   }
 }
 
-void RegisterAllocator::BlockRegister(Location location,
-                                      size_t start,
-                                      size_t end) {
+void RegisterAllocator::BlockRegister(Location location, size_t start, size_t end) {
   int reg = location.reg();
   DCHECK(location.IsRegister() || location.IsFpuRegister());
   LiveInterval* interval = location.IsRegister()
@@ -147,6 +146,19 @@
   interval->AddRange(start, end);
 }
 
+void RegisterAllocator::BlockRegisters(size_t start, size_t end, bool caller_save_only) {
+  for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) {
+    if (!caller_save_only || !codegen_->IsCoreCalleeSaveRegister(i)) {
+      BlockRegister(Location::RegisterLocation(i), start, end);
+    }
+  }
+  for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) {
+    if (!caller_save_only || !codegen_->IsFloatingPointCalleeSaveRegister(i)) {
+      BlockRegister(Location::FpuRegisterLocation(i), start, end);
+    }
+  }
+}
+
 void RegisterAllocator::AllocateRegistersInternal() {
   // Iterate post-order, to ensure the list is sorted, and the last added interval
   // is the one with the lowest start position.
@@ -159,6 +171,13 @@
     for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
       ProcessInstruction(inst_it.Current());
     }
+
+    if (block->IsCatchBlock()) {
+      // By blocking all registers at the top of each catch block, we force
+      // intervals used after catch to spill.
+      size_t position = block->GetLifetimeStart();
+      BlockRegisters(position, position + 1);
+    }
   }
 
   number_of_registers_ = codegen_->GetNumberOfCoreRegisters();
@@ -275,21 +294,7 @@
   }
 
   if (locations->WillCall()) {
-    // Block all registers.
-    for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) {
-      if (!codegen_->IsCoreCalleeSaveRegister(i)) {
-        BlockRegister(Location::RegisterLocation(i),
-                      position,
-                      position + 1);
-      }
-    }
-    for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) {
-      if (!codegen_->IsFloatingPointCalleeSaveRegister(i)) {
-        BlockRegister(Location::FpuRegisterLocation(i),
-                      position,
-                      position + 1);
-      }
-    }
+    BlockRegisters(position, position + 1, /* caller_save_only */ true);
   }
 
   for (size_t i = 0; i < instruction->InputCount(); ++i) {
@@ -378,6 +383,10 @@
     DCHECK(output.IsUnallocated() || output.IsConstant());
   }
 
+  if (instruction->IsPhi() && instruction->AsPhi()->IsCatchPhi()) {
+    AllocateSpillSlotForCatchPhi(instruction->AsPhi());
+  }
+
   // If needed, add interval to the list of unhandled intervals.
   if (current->HasSpillSlot() || instruction->IsConstant()) {
     // Split just before first register use.
@@ -1212,14 +1221,13 @@
    * moves in B3.
    */
   if (block_from->GetDominator() != nullptr) {
-    const GrowableArray<HBasicBlock*>& dominated = block_from->GetDominator()->GetDominatedBlocks();
-    for (size_t i = 0; i < dominated.Size(); ++i) {
-      size_t position = dominated.Get(i)->GetLifetimeStart();
+    for (HBasicBlock* dominated : block_from->GetDominator()->GetDominatedBlocks()) {
+      size_t position = dominated->GetLifetimeStart();
       if ((position > from) && (block_to->GetLifetimeStart() > position)) {
         // Even if we found a better block, we continue iterating in case
         // a dominated block is closer.
         // Note that dominated blocks are not sorted in liveness order.
-        block_to = dominated.Get(i);
+        block_to = dominated;
         DCHECK_NE(block_to, block_from);
       }
     }
@@ -1283,6 +1291,8 @@
   }
 
   HInstruction* defined_by = parent->GetDefinedBy();
+  DCHECK(!defined_by->IsPhi() || !defined_by->AsPhi()->IsCatchPhi());
+
   if (defined_by->IsParameterValue()) {
     // Parameters have their own stack slot.
     parent->SetSpillSlot(codegen_->GetStackSlotOfParameter(defined_by->AsParameterValue()));
@@ -1299,12 +1309,6 @@
     return;
   }
 
-  LiveInterval* last_sibling = interval;
-  while (last_sibling->GetNextSibling() != nullptr) {
-    last_sibling = last_sibling->GetNextSibling();
-  }
-  size_t end = last_sibling->GetEnd();
-
   GrowableArray<size_t>* spill_slots = nullptr;
   switch (interval->GetType()) {
     case Primitive::kPrimDouble:
@@ -1337,6 +1341,7 @@
     }
   }
 
+  size_t end = interval->GetLastSibling()->GetEnd();
   if (parent->NeedsTwoSpillSlots()) {
     if (slot == spill_slots->Size()) {
       // We need a new spill slot.
@@ -1372,6 +1377,28 @@
       || destination.IsDoubleStackSlot();
 }
 
+void RegisterAllocator::AllocateSpillSlotForCatchPhi(HPhi* phi) {
+  LiveInterval* interval = phi->GetLiveInterval();
+
+  HInstruction* previous_phi = phi->GetPrevious();
+  DCHECK(previous_phi == nullptr ||
+         previous_phi->AsPhi()->GetRegNumber() <= phi->GetRegNumber())
+      << "Phis expected to be sorted by vreg number, so that equivalent phis are adjacent.";
+
+  if (phi->IsVRegEquivalentOf(previous_phi)) {
+    // This is an equivalent of the previous phi. We need to assign the same
+    // catch phi slot.
+    DCHECK(previous_phi->GetLiveInterval()->HasSpillSlot());
+    interval->SetSpillSlot(previous_phi->GetLiveInterval()->GetSpillSlot());
+  } else {
+    // Allocate a new spill slot for this catch phi.
+    // TODO: Reuse spill slots when intervals of phis from different catch
+    //       blocks do not overlap.
+    interval->SetSpillSlot(catch_phi_spill_slots_);
+    catch_phi_spill_slots_ += interval->NeedsTwoSpillSlots() ? 2 : 1;
+  }
+}
+
 void RegisterAllocator::AddMove(HParallelMove* move,
                                 Location source,
                                 Location destination,
@@ -1498,7 +1525,7 @@
   DCHECK(IsValidDestination(destination)) << destination;
   if (source.Equals(destination)) return;
 
-  DCHECK_EQ(block->GetSuccessors().Size(), 1u);
+  DCHECK_EQ(block->NumberOfNormalSuccessors(), 1u);
   HInstruction* last = block->GetLastInstruction();
   // We insert moves at exit for phi predecessors and connecting blocks.
   // A block ending with an if cannot branch to a block with phis because
@@ -1725,13 +1752,13 @@
 
   // If `from` has only one successor, we can put the moves at the exit of it. Otherwise
   // we need to put the moves at the entry of `to`.
-  if (from->GetSuccessors().Size() == 1) {
+  if (from->NumberOfNormalSuccessors() == 1) {
     InsertParallelMoveAtExitOf(from,
                                interval->GetParent()->GetDefinedBy(),
                                source->ToLocation(),
                                destination->ToLocation());
   } else {
-    DCHECK_EQ(to->GetPredecessors().Size(), 1u);
+    DCHECK_EQ(to->GetPredecessors().size(), 1u);
     InsertParallelMoveAtEntryOf(to,
                                 interval->GetParent()->GetDefinedBy(),
                                 source->ToLocation(),
@@ -1769,17 +1796,25 @@
     } else if (instruction->IsCurrentMethod()) {
       // The current method is always at offset 0.
       DCHECK(!current->HasSpillSlot() || (current->GetSpillSlot() == 0));
+    } else if (instruction->IsPhi() && instruction->AsPhi()->IsCatchPhi()) {
+      DCHECK(current->HasSpillSlot());
+      size_t slot = current->GetSpillSlot()
+                    + GetNumberOfSpillSlots()
+                    + reserved_out_slots_
+                    - catch_phi_spill_slots_;
+      current->SetSpillSlot(slot * kVRegSize);
     } else if (current->HasSpillSlot()) {
       // Adjust the stack slot, now that we know the number of them for each type.
       // The way this implementation lays out the stack is the following:
-      // [parameter slots     ]
-      // [double spill slots  ]
-      // [long spill slots    ]
-      // [float spill slots   ]
-      // [int/ref values      ]
-      // [maximum out values  ] (number of arguments for calls)
-      // [art method          ].
-      uint32_t slot = current->GetSpillSlot();
+      // [parameter slots       ]
+      // [catch phi spill slots ]
+      // [double spill slots    ]
+      // [long spill slots      ]
+      // [float spill slots     ]
+      // [int/ref values        ]
+      // [maximum out values    ] (number of arguments for calls)
+      // [art method            ].
+      size_t slot = current->GetSpillSlot();
       switch (current->GetType()) {
         case Primitive::kPrimDouble:
           slot += long_spill_slots_.Size();
@@ -1829,12 +1864,22 @@
   // Resolve non-linear control flow across branches. Order does not matter.
   for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
-    BitVector* live = liveness_.GetLiveInSet(*block);
-    for (uint32_t idx : live->Indexes()) {
-      HInstruction* current = liveness_.GetInstructionFromSsaIndex(idx);
-      LiveInterval* interval = current->GetLiveInterval();
-      for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
-        ConnectSplitSiblings(interval, block->GetPredecessors().Get(i), block);
+    if (block->IsCatchBlock()) {
+      // Instructions live at the top of catch blocks were forced to spill.
+      if (kIsDebugBuild) {
+        BitVector* live = liveness_.GetLiveInSet(*block);
+        for (uint32_t idx : live->Indexes()) {
+          LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval();
+          DCHECK(!interval->GetSiblingAt(block->GetLifetimeStart())->HasRegister());
+        }
+      }
+    } else {
+      BitVector* live = liveness_.GetLiveInSet(*block);
+      for (uint32_t idx : live->Indexes()) {
+        LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval();
+        for (HBasicBlock* predecessor : block->GetPredecessors()) {
+          ConnectSplitSiblings(interval, predecessor, block);
+        }
       }
     }
   }
@@ -1842,16 +1887,20 @@
   // Resolve phi inputs. Order does not matter.
   for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) {
     HBasicBlock* current = it.Current();
-    for (HInstructionIterator inst_it(current->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
-      HInstruction* phi = inst_it.Current();
-      for (size_t i = 0, e = current->GetPredecessors().Size(); i < e; ++i) {
-        HBasicBlock* predecessor = current->GetPredecessors().Get(i);
-        DCHECK_EQ(predecessor->GetSuccessors().Size(), 1u);
-        HInstruction* input = phi->InputAt(i);
-        Location source = input->GetLiveInterval()->GetLocationAt(
-            predecessor->GetLifetimeEnd() - 1);
-        Location destination = phi->GetLiveInterval()->ToLocation();
-        InsertParallelMoveAtExitOf(predecessor, phi, source, destination);
+    if (current->IsCatchBlock()) {
+      // Catch phi values are set at runtime by the exception delivery mechanism.
+    } else {
+      for (HInstructionIterator inst_it(current->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
+        HInstruction* phi = inst_it.Current();
+        for (size_t i = 0, e = current->GetPredecessors().size(); i < e; ++i) {
+          HBasicBlock* predecessor = current->GetPredecessor(i);
+          DCHECK_EQ(predecessor->NumberOfNormalSuccessors(), 1u);
+          HInstruction* input = phi->InputAt(i);
+          Location source = input->GetLiveInterval()->GetLocationAt(
+              predecessor->GetLifetimeEnd() - 1);
+          Location destination = phi->GetLiveInterval()->ToLocation();
+          InsertParallelMoveAtExitOf(predecessor, phi, source, destination);
+        }
       }
     }
   }
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index c29fe75..e030464 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -29,6 +29,7 @@
 class HGraph;
 class HInstruction;
 class HParallelMove;
+class HPhi;
 class LiveInterval;
 class Location;
 class SsaLivenessAnalysis;
@@ -72,7 +73,8 @@
     return int_spill_slots_.Size()
         + long_spill_slots_.Size()
         + float_spill_slots_.Size()
-        + double_spill_slots_.Size();
+        + double_spill_slots_.Size()
+        + catch_phi_spill_slots_;
   }
 
   static constexpr const char* kRegisterAllocatorPassName = "register";
@@ -99,10 +101,17 @@
 
   // Update the interval for the register in `location` to cover [start, end).
   void BlockRegister(Location location, size_t start, size_t end);
+  void BlockRegisters(size_t start, size_t end, bool caller_save_only = false);
 
-  // Allocate a spill slot for the given interval.
+  // Allocate a spill slot for the given interval. Should be called in linear
+  // order of interval starting positions.
   void AllocateSpillSlotFor(LiveInterval* interval);
 
+  // Allocate a spill slot for the given catch phi. Will allocate the same slot
+  // for phis which share the same vreg. Must be called in reverse linear order
+  // of lifetime positions and ascending vreg numbers for correctness.
+  void AllocateSpillSlotForCatchPhi(HPhi* phi);
+
   // Connect adjacent siblings within blocks.
   void ConnectSiblings(LiveInterval* interval);
 
@@ -202,6 +211,11 @@
   GrowableArray<size_t> float_spill_slots_;
   GrowableArray<size_t> double_spill_slots_;
 
+  // Spill slots allocated to catch phis. This category is special-cased because
+  // (1) slots are allocated prior to linear scan and in reverse linear order,
+  // (2) equivalent phis need to share slots despite having different types.
+  size_t catch_phi_spill_slots_;
+
   // Instructions that need a safepoint.
   GrowableArray<HInstruction*> safepoints_;
 
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 561c3b4..e6209b9 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -241,8 +241,8 @@
     HBasicBlock* block = loop_headers_.Get(i);
     for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
       HPhi* phi = it.Current()->AsPhi();
-      for (size_t pred = 0; pred < block->GetPredecessors().Size(); pred++) {
-        HInstruction* input = ValueOfLocal(block->GetPredecessors().Get(pred), phi->GetRegNumber());
+      for (HBasicBlock* predecessor : block->GetPredecessors()) {
+        HInstruction* input = ValueOfLocal(predecessor, phi->GetRegNumber());
         phi->AddInput(input);
       }
     }
@@ -369,16 +369,16 @@
     // Save the loop header so that the last phase of the analysis knows which
     // blocks need to be updated.
     loop_headers_.Add(block);
-  } else if (block->GetPredecessors().Size() > 0) {
+  } else if (block->GetPredecessors().size() > 0) {
     // All predecessors have already been visited because we are visiting in reverse post order.
     // We merge the values of all locals, creating phis if those values differ.
     for (size_t local = 0; local < current_locals_->Size(); local++) {
       bool one_predecessor_has_no_value = false;
       bool is_different = false;
-      HInstruction* value = ValueOfLocal(block->GetPredecessors().Get(0), local);
+      HInstruction* value = ValueOfLocal(block->GetPredecessor(0), local);
 
-      for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
-        HInstruction* current = ValueOfLocal(block->GetPredecessors().Get(i), local);
+      for (HBasicBlock* predecessor : block->GetPredecessors()) {
+        HInstruction* current = ValueOfLocal(predecessor, local);
         if (current == nullptr) {
           one_predecessor_has_no_value = true;
           break;
@@ -395,9 +395,9 @@
 
       if (is_different) {
         HPhi* phi = new (GetGraph()->GetArena()) HPhi(
-            GetGraph()->GetArena(), local, block->GetPredecessors().Size(), Primitive::kPrimVoid);
-        for (size_t i = 0; i < block->GetPredecessors().Size(); i++) {
-          HInstruction* pred_value = ValueOfLocal(block->GetPredecessors().Get(i), local);
+            GetGraph()->GetArena(), local, block->GetPredecessors().size(), Primitive::kPrimVoid);
+        for (size_t i = 0; i < block->GetPredecessors().size(); i++) {
+          HInstruction* pred_value = ValueOfLocal(block->GetPredecessor(i), local);
           phi->SetRawInputAt(i, pred_value);
         }
         block->AddPhi(phi);
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 40502c1..63635f3 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -73,7 +73,7 @@
   forward_predecessors.SetSize(graph_->GetBlocks().Size());
   for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
-    size_t number_of_forward_predecessors = block->GetPredecessors().Size();
+    size_t number_of_forward_predecessors = block->GetPredecessors().size();
     if (block->IsLoopHeader()) {
       number_of_forward_predecessors -= block->GetLoopInformation()->NumberOfBackEdges();
     }
@@ -89,8 +89,7 @@
   do {
     HBasicBlock* current = worklist.Pop();
     graph_->linear_order_.Add(current);
-    for (size_t i = 0, e = current->GetSuccessors().Size(); i < e; ++i) {
-      HBasicBlock* successor = current->GetSuccessors().Get(i);
+    for (HBasicBlock* successor : current->GetSuccessors()) {
       int block_id = successor->GetBlockId();
       size_t number_of_remaining_predecessors = forward_predecessors.Get(block_id);
       if (number_of_remaining_predecessors == 1) {
@@ -185,17 +184,27 @@
 
     // Set phi inputs of successors of this block corresponding to this block
     // as live_in.
-    for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) {
-      HBasicBlock* successor = block->GetSuccessors().Get(i);
+    for (HBasicBlock* successor : block->GetSuccessors()) {
       live_in->Union(GetLiveInSet(*successor));
-      size_t phi_input_index = successor->GetPredecessorIndexOf(block);
-      for (HInstructionIterator inst_it(successor->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
-        HInstruction* phi = inst_it.Current();
-        HInstruction* input = phi->InputAt(phi_input_index);
-        input->GetLiveInterval()->AddPhiUse(phi, phi_input_index, block);
-        // A phi input whose last user is the phi dies at the end of the predecessor block,
-        // and not at the phi's lifetime position.
-        live_in->SetBit(input->GetSsaIndex());
+      if (successor->IsCatchBlock()) {
+        // Inputs of catch phis will be kept alive through their environment
+        // uses, allowing the runtime to copy their values to the corresponding
+        // catch phi spill slots when an exception is thrown.
+        // The only instructions which may not be recorded in the environments
+        // are constants created by the SSA builder as typed equivalents of
+        // untyped constants from the bytecode, or phis with only such constants
+        // as inputs (verified by SSAChecker). Their raw binary value must
+        // therefore be the same and we only need to keep alive one.
+      } else {
+        size_t phi_input_index = successor->GetPredecessorIndexOf(block);
+        for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+          HInstruction* phi = phi_it.Current();
+          HInstruction* input = phi->InputAt(phi_input_index);
+          input->GetLiveInterval()->AddPhiUse(phi, phi_input_index, block);
+          // A phi input whose last user is the phi dies at the end of the predecessor block,
+          // and not at the phi's lifetime position.
+          live_in->SetBit(input->GetSsaIndex());
+        }
       }
     }
 
@@ -296,8 +305,7 @@
   BitVector* live_out = GetLiveOutSet(block);
   bool changed = false;
   // The live_out set of a block is the union of live_in sets of its successors.
-  for (size_t i = 0, e = block.GetSuccessors().Size(); i < e; ++i) {
-    HBasicBlock* successor = block.GetSuccessors().Get(i);
+  for (HBasicBlock* successor : block.GetSuccessors()) {
     if (live_out->Union(GetLiveInSet(*successor))) {
       changed = true;
     }
@@ -342,8 +350,8 @@
     // will avoid a move between the two blocks.
     HBasicBlock* block = liveness.GetBlockFromPosition(GetStart() / 2);
     size_t next_register_use = FirstRegisterUse();
-    for (size_t i = 0; i < block->GetPredecessors().Size(); ++i) {
-      size_t position = block->GetPredecessors().Get(i)->GetLifetimeEnd() - 1;
+    for (HBasicBlock* predecessor : block->GetPredecessors()) {
+      size_t position = predecessor->GetLifetimeEnd() - 1;
       // We know positions above GetStart() do not have a location yet.
       if (position < GetStart()) {
         LiveInterval* existing = GetParent()->GetSiblingAt(position);
@@ -376,17 +384,16 @@
             return reg;
           }
         }
-        const GrowableArray<HBasicBlock*>& predecessors = user->GetBlock()->GetPredecessors();
         // If the instruction dies at the phi assignment, we can try having the
         // same register.
-        if (end == predecessors.Get(input_index)->GetLifetimeEnd()) {
+        if (end == user->GetBlock()->GetPredecessor(input_index)->GetLifetimeEnd()) {
           for (size_t i = 0, e = user->InputCount(); i < e; ++i) {
             if (i == input_index) {
               continue;
             }
             HInstruction* input = user->InputAt(i);
             Location location = input->GetLiveInterval()->GetLocationAt(
-                predecessors.Get(i)->GetLifetimeEnd() - 1);
+                user->GetBlock()->GetPredecessor(i)->GetLifetimeEnd() - 1);
             if (location.IsRegisterKind()) {
               int reg = RegisterOrLowRegister(location);
               if (free_until[reg] >= use_position) {
@@ -420,10 +427,11 @@
 int LiveInterval::FindHintAtDefinition() const {
   if (defined_by_->IsPhi()) {
     // Try to use the same register as one of the inputs.
-    const GrowableArray<HBasicBlock*>& predecessors = defined_by_->GetBlock()->GetPredecessors();
+    const ArenaVector<HBasicBlock*>& predecessors = defined_by_->GetBlock()->GetPredecessors();
     for (size_t i = 0, e = defined_by_->InputCount(); i < e; ++i) {
       HInstruction* input = defined_by_->InputAt(i);
-      size_t end = predecessors.Get(i)->GetLifetimeEnd();
+      DCHECK_LT(i, predecessors.size());
+      size_t end = predecessors[i]->GetLifetimeEnd();
       LiveInterval* input_interval = input->GetLiveInterval()->GetSiblingAt(end - 1);
       if (input_interval->GetEnd() == end) {
         // If the input dies at the end of the predecessor, we know its register can
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index a7044de..ef396cb 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -1209,6 +1209,9 @@
     // A value that's not live in compiled code may still be needed in interpreter,
     // due to code motion, etc.
     if (env_holder->IsDeoptimize()) return true;
+    // A value live at a throwing instruction in a try block may be copied by
+    // the exception handler to its location at the top of the catch block.
+    if (env_holder->CanThrowIntoCatchBlock()) return true;
     if (instruction->GetBlock()->GetGraph()->IsDebuggable()) return true;
     return instruction->GetType() == Primitive::kPrimNot;
   }
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 1f1530f..1f0bac5 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -286,7 +286,7 @@
         stack_map.SetDexRegisterMapOffset(
             stack_map_encoding_,
             code_info.GetStackMapAt(entry.same_dex_register_map_as_, stack_map_encoding_)
-                     .GetDexRegisterMapOffset(stack_map_encoding_));
+                .GetDexRegisterMapOffset(stack_map_encoding_));
       } else {
         // New dex registers maps should be added to the stack map.
         MemoryRegion register_region = dex_register_locations_region.Subregion(
diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc
index 5ca66a1..e745d94 100644
--- a/compiler/optimizing/suspend_check_test.cc
+++ b/compiler/optimizing/suspend_check_test.cc
@@ -36,7 +36,7 @@
   bool graph_built = builder.BuildGraph(*item);
   ASSERT_TRUE(graph_built);
 
-  HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessors().Get(0);
+  HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessor(0);
   HInstruction* first_instruction = first_block->GetFirstInstruction();
   // Account for some tests having a store local as first instruction.
   ASSERT_TRUE(first_instruction->IsSuspendCheck()
diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h
index b1d7b4c..b6a228c 100644
--- a/compiler/utils/test_dex_file_builder.h
+++ b/compiler/utils/test_dex_file_builder.h
@@ -216,7 +216,7 @@
     std::unique_ptr<const DexFile> dex_file(DexFile::Open(
         &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, &error_msg));
     CHECK(dex_file != nullptr) << error_msg;
-    return std::move(dex_file);
+    return dex_file;
   }
 
   uint32_t GetStringIdx(const std::string& type) {
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index e6710ed..f10799c 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -326,9 +326,9 @@
           [referrer] "r"(referrer)
         : "at", "v0", "v1", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1",
           "fp", "ra",
-          "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13",
-          "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26",
-          "f27", "f28", "f29", "f30", "f31",
+          "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$f10", "$f11",
+          "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22",
+          "$f23", "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
           "memory");  // clobber.
 #elif defined(__mips__) && defined(__LP64__)
     __asm__ __volatile__ (
@@ -680,9 +680,9 @@
           [referrer] "r"(referrer), [hidden] "r"(hidden)
         : "at", "v0", "v1", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1",
           "fp", "ra",
-          "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13",
-          "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26",
-          "f27", "f28", "f29", "f30", "f31",
+          "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$f10", "$f11",
+          "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22",
+          "$f23", "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
           "memory");  // clobber.
 #elif defined(__mips__) && defined(__LP64__)
     __asm__ __volatile__ (
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 5dbea52..26839ec 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -223,28 +223,48 @@
   return DexFile::kDexNoIndex;
 }
 
-uintptr_t ArtMethod::ToNativeQuickPc(const uint32_t dex_pc, bool abort_on_failure) {
+uintptr_t ArtMethod::ToNativeQuickPc(const uint32_t dex_pc,
+                                     bool is_catch_handler,
+                                     bool abort_on_failure) {
   const void* entry_point = GetQuickOatEntryPoint(sizeof(void*));
-  MappingTable table(entry_point != nullptr ?
-      GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
-  if (table.TotalSize() == 0) {
-    DCHECK_EQ(dex_pc, 0U);
-    return 0;   // Special no mapping/pc == 0 case
-  }
-  // Assume the caller wants a dex-to-pc mapping so check here first.
-  typedef MappingTable::DexToPcIterator It;
-  for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
-    if (cur.DexPc() == dex_pc) {
-      return reinterpret_cast<uintptr_t>(entry_point) + cur.NativePcOffset();
+  if (IsOptimized(sizeof(void*))) {
+    // Optimized code does not have a mapping table. Search for the dex-to-pc
+    // mapping in stack maps.
+    CodeInfo code_info = GetOptimizedCodeInfo();
+    StackMapEncoding encoding = code_info.ExtractEncoding();
+
+    // All stack maps are stored in the same CodeItem section, safepoint stack
+    // maps first, then catch stack maps. We use `is_catch_dex_pc` to select the
+    // order of iteration.
+    StackMap stack_map =
+        LIKELY(is_catch_handler) ? code_info.GetCatchStackMapForDexPc(dex_pc, encoding)
+                                 : code_info.GetStackMapForDexPc(dex_pc, encoding);
+    if (stack_map.IsValid()) {
+      return reinterpret_cast<uintptr_t>(entry_point) + stack_map.GetNativePcOffset(encoding);
+    }
+  } else {
+    MappingTable table(entry_point != nullptr ?
+        GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
+    if (table.TotalSize() == 0) {
+      DCHECK_EQ(dex_pc, 0U);
+      return 0;   // Special no mapping/pc == 0 case
+    }
+    // Assume the caller wants a dex-to-pc mapping so check here first.
+    typedef MappingTable::DexToPcIterator It;
+    for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
+      if (cur.DexPc() == dex_pc) {
+        return reinterpret_cast<uintptr_t>(entry_point) + cur.NativePcOffset();
+      }
+    }
+    // Now check pc-to-dex mappings.
+    typedef MappingTable::PcToDexIterator It2;
+    for (It2 cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
+      if (cur.DexPc() == dex_pc) {
+        return reinterpret_cast<uintptr_t>(entry_point) + cur.NativePcOffset();
+      }
     }
   }
-  // Now check pc-to-dex mappings.
-  typedef MappingTable::PcToDexIterator It2;
-  for (It2 cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
-    if (cur.DexPc() == dex_pc) {
-      return reinterpret_cast<uintptr_t>(entry_point) + cur.NativePcOffset();
-    }
-  }
+
   if (abort_on_failure) {
     LOG(FATAL) << "Failed to find native offset for dex pc 0x" << std::hex << dex_pc
                << " in " << PrettyMethod(this);
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 3f2161f..6c3b13f 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -442,7 +442,9 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Converts a dex PC to a native PC.
-  uintptr_t ToNativeQuickPc(const uint32_t dex_pc, bool abort_on_failure = true)
+  uintptr_t ToNativeQuickPc(const uint32_t dex_pc,
+                            bool is_catch_handler,
+                            bool abort_on_failure = true)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   MethodReference ToMethodReference() SHARED_REQUIRES(Locks::mutator_lock_) {
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 3a4bccd..a36b0fb 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -52,13 +52,14 @@
   "SSA2Dalvik   ",
   "Dalvik2SSA   ",
   "DebugInfo    ",
-  "Successor    ",
   "RegAlloc     ",
   "Data         ",
-  "Preds        ",
   "STL          ",
   "Graph        ",
   "BasicBlock   ",
+  "Predecessors ",
+  "Successors   ",
+  "Dominated    ",
   "Instruction  ",
   "LoopInfo     ",
   "TryCatchInf  ",
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index af2bfbc..47defb4 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -62,13 +62,14 @@
   kArenaAllocSSAToDalvikMap,
   kArenaAllocDalvikToSSAMap,
   kArenaAllocDebugInfo,
-  kArenaAllocSuccessor,
   kArenaAllocRegAlloc,
   kArenaAllocData,
-  kArenaAllocPredecessors,
   kArenaAllocSTL,
   kArenaAllocGraph,
   kArenaAllocBasicBlock,
+  kArenaAllocPredecessors,
+  kArenaAllocSuccessors,
+  kArenaAllocDominated,
   kArenaAllocInstruction,
   kArenaAllocLoopInfo,
   kArenaAllocTryCatchInfo,
diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h
index 901f25f..0949619 100644
--- a/runtime/base/stl_util.h
+++ b/runtime/base/stl_util.h
@@ -20,6 +20,8 @@
 #include <algorithm>
 #include <sstream>
 
+#include "base/logging.h"
+
 namespace art {
 
 // Sort and remove duplicates of an STL vector or deque.
@@ -94,6 +96,59 @@
   return os.str();
 }
 
+// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
+struct FreeDelete {
+  // NOTE: Deleting a const object is valid but free() takes a non-const pointer.
+  void operator()(const void* ptr) const {
+    free(const_cast<void*>(ptr));
+  }
+};
+
+// Alias for std::unique_ptr<> that uses the C function free() to delete objects.
+template <typename T>
+using UniqueCPtr = std::unique_ptr<T, FreeDelete>;
+
+// C++14 from-the-future import (std::make_unique)
+// Invoke the constructor of 'T' with the provided args, and wrap the result in a unique ptr.
+template <typename T, typename ... Args>
+std::unique_ptr<T> MakeUnique(Args&& ... args) {
+  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+// Find index of the first element with the specified value known to be in the container.
+template <typename Container, typename T>
+size_t IndexOfElement(const Container& container, const T& value) {
+  auto it = std::find(container.begin(), container.end(), value);
+  DCHECK(it != container.end());  // Must exist.
+  return std::distance(container.begin(), it);
+}
+
+// Remove the first element with the specified value known to be in the container.
+template <typename Container, typename T>
+void RemoveElement(Container& container, const T& value) {
+  auto it = std::find(container.begin(), container.end(), value);
+  DCHECK(it != container.end());  // Must exist.
+  container.erase(it);
+}
+
+// Replace the first element with the specified old_value known to be in the container.
+template <typename Container, typename T>
+void ReplaceElement(Container& container, const T& old_value, const T& new_value) {
+  auto it = std::find(container.begin(), container.end(), old_value);
+  DCHECK(it != container.end());  // Must exist.
+  *it = new_value;
+}
+
+// Search for an element with the specified value and return true if it was found, false otherwise.
+template <typename Container, typename T>
+bool ContainsElement(const Container& container, const T& value, size_t start_pos = 0u) {
+  DCHECK_LE(start_pos, container.size());
+  auto start = container.begin();
+  std::advance(start, start_pos);
+  auto it = std::find(start, container.end(), value);
+  return it != container.end();
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_STL_UTIL_H_
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 4cb9a3b..ae62e2b 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -31,6 +31,7 @@
 #include "art_method-inl.h"
 #include "base/hash_map.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/stringprintf.h"
 #include "class_linker-inl.h"
 #include "dex_file-inl.h"
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 33d756e..9f84bd2 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -186,13 +186,15 @@
     fake_stack.push_back(0);
   }
 
-  fake_stack.push_back(method_g_->ToNativeQuickPc(dex_pc));  // return pc
+  fake_stack.push_back(
+      method_g_->ToNativeQuickPc(dex_pc, /* is_catch_handler */ false));  // return pc
 
   // Create/push fake 16byte stack frame for method g
   fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_));
   fake_stack.push_back(0);
   fake_stack.push_back(0);
-  fake_stack.push_back(method_f_->ToNativeQuickPc(dex_pc));  // return pc
+  fake_stack.push_back(
+      method_g_->ToNativeQuickPc(dex_pc, /* is_catch_handler */ false));  // return pc
 
   // Create/push fake 16byte stack frame for method f
   fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_));
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 57af959..4cbcdc5 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -1073,9 +1073,14 @@
   if (to_ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->IsTypeOfReferenceClass() &&
       to_ref->AsReference()->GetReferent<kWithoutReadBarrier>() != nullptr &&
       !IsInToSpace(to_ref->AsReference()->GetReferent<kWithoutReadBarrier>())) {
-    // Leave References gray so that GetReferent() will trigger RB.
+    // Leave this Reference gray in the queue so that GetReferent() will trigger a read barrier. We
+    // will change it to black or white later in ReferenceQueue::DequeuePendingReference().
     CHECK(to_ref->AsReference()->IsEnqueued()) << "Left unenqueued ref gray " << to_ref;
   } else {
+    // We may occasionally leave a Reference black or white in the queue if its referent happens to
+    // be concurrently marked after the Scan() call above has enqueued the Reference, in which case
+    // the above IsInToSpace() evaluates to true and we change the color from gray to black or white
+    // here in this else block.
 #ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
     if (kUseBakerReadBarrier) {
       if (region_space_->IsInToSpace(to_ref)) {
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index f505428..0c0ab6d 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -89,19 +89,36 @@
   Heap* heap = Runtime::Current()->GetHeap();
   if (kUseBakerOrBrooksReadBarrier && heap->CurrentCollectorType() == kCollectorTypeCC &&
       heap->ConcurrentCopyingCollector()->IsActive()) {
-    // Clear the gray ptr we left in ConcurrentCopying::ProcessMarkStack().
-    // We don't want to do this when the zygote compaction collector (SemiSpace) is running.
+    // Change the gray ptr we left in ConcurrentCopying::ProcessMarkStackRef() to black or white.
+    // We check IsActive() above because we don't want to do this when the zygote compaction
+    // collector (SemiSpace) is running.
     CHECK(ref != nullptr);
-    CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr())
-        << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer();
-    if (heap->ConcurrentCopyingCollector()->RegionSpace()->IsInToSpace(ref)) {
-      // Moving objects.
-      ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), ReadBarrier::WhitePtr());
-      CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr());
+    collector::ConcurrentCopying* concurrent_copying = heap->ConcurrentCopyingCollector();
+    const bool is_moving = concurrent_copying->RegionSpace()->IsInToSpace(ref);
+    if (ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr()) {
+      if (is_moving) {
+        ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), ReadBarrier::WhitePtr());
+        CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr());
+      } else {
+        ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), ReadBarrier::BlackPtr());
+        CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr());
+      }
     } else {
-      // Non-moving objects.
-      ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), ReadBarrier::BlackPtr());
-      CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr());
+      // In ConcurrentCopying::ProcessMarkStackRef() we may leave a black or white Reference in the
+      // queue and find it here, which is OK. Check that the color makes sense depending on whether
+      // the Reference is moving or not and that the referent has been marked.
+      if (is_moving) {
+        CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr())
+            << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer();
+      } else {
+        CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr())
+            << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer();
+      }
+      mirror::Object* referent = ref->GetReferent<kWithoutReadBarrier>();
+      CHECK(referent != nullptr) << "Reference should not have been enqueued if referent is null";
+      CHECK(concurrent_copying->IsInToSpace(referent))
+          << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer()
+          << " referent=" << referent;
     }
   }
   return ref;
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 57c6866..d797d2a 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -96,7 +96,8 @@
       if (found_dex_pc != DexFile::kDexNoIndex) {
         exception_handler_->SetHandlerMethod(method);
         exception_handler_->SetHandlerDexPc(found_dex_pc);
-        exception_handler_->SetHandlerQuickFramePc(method->ToNativeQuickPc(found_dex_pc));
+        exception_handler_->SetHandlerQuickFramePc(
+            method->ToNativeQuickPc(found_dex_pc, /* is_catch_handler */ true));
         exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
         return false;  // End stack walk.
       }
@@ -144,6 +145,107 @@
     // Put exception back in root set with clear throw location.
     self_->SetException(exception_ref.Get());
   }
+  // If the handler is in optimized code, we need to set the catch environment.
+  if (*handler_quick_frame_ != nullptr &&
+      handler_method_ != nullptr &&
+      handler_method_->IsOptimized(sizeof(void*))) {
+    SetCatchEnvironmentForOptimizedHandler(&visitor);
+  }
+}
+
+static VRegKind ToVRegKind(DexRegisterLocation::Kind kind) {
+  // Slightly hacky since we cannot map DexRegisterLocationKind and VRegKind
+  // one to one. However, StackVisitor::GetVRegFromOptimizedCode only needs to
+  // distinguish between core/FPU registers and low/high bits on 64-bit.
+  switch (kind) {
+    case DexRegisterLocation::Kind::kConstant:
+    case DexRegisterLocation::Kind::kInStack:
+      // VRegKind is ignored.
+      return VRegKind::kUndefined;
+
+    case DexRegisterLocation::Kind::kInRegister:
+      // Selects core register. For 64-bit registers, selects low 32 bits.
+      return VRegKind::kLongLoVReg;
+
+    case DexRegisterLocation::Kind::kInRegisterHigh:
+      // Selects core register. For 64-bit registers, selects high 32 bits.
+      return VRegKind::kLongHiVReg;
+
+    case DexRegisterLocation::Kind::kInFpuRegister:
+      // Selects FPU register. For 64-bit registers, selects low 32 bits.
+      return VRegKind::kDoubleLoVReg;
+
+    case DexRegisterLocation::Kind::kInFpuRegisterHigh:
+      // Selects FPU register. For 64-bit registers, selects high 32 bits.
+      return VRegKind::kDoubleHiVReg;
+
+    default:
+      LOG(FATAL) << "Unexpected vreg location "
+                 << DexRegisterLocation::PrettyDescriptor(kind);
+      UNREACHABLE();
+  }
+}
+
+void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* stack_visitor) {
+  DCHECK(!is_deoptimization_);
+  DCHECK(*handler_quick_frame_ != nullptr) << "Method should not be called on upcall exceptions";
+  DCHECK(handler_method_ != nullptr && handler_method_->IsOptimized(sizeof(void*)));
+
+  if (kDebugExceptionDelivery) {
+    self_->DumpStack(LOG(INFO) << "Setting catch phis: ");
+  }
+
+  const size_t number_of_vregs = handler_method_->GetCodeItem()->registers_size_;
+  CodeInfo code_info = handler_method_->GetOptimizedCodeInfo();
+  StackMapEncoding encoding = code_info.ExtractEncoding();
+
+  // Find stack map of the throwing instruction.
+  StackMap throw_stack_map =
+      code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset(), encoding);
+  DCHECK(throw_stack_map.IsValid());
+  DexRegisterMap throw_vreg_map =
+      code_info.GetDexRegisterMapOf(throw_stack_map, encoding, number_of_vregs);
+
+  // Find stack map of the catch block.
+  StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc(), encoding);
+  DCHECK(catch_stack_map.IsValid());
+  DexRegisterMap catch_vreg_map =
+      code_info.GetDexRegisterMapOf(catch_stack_map, encoding, number_of_vregs);
+
+  // Copy values between them.
+  for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) {
+    DexRegisterLocation::Kind catch_location =
+        catch_vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding);
+    if (catch_location == DexRegisterLocation::Kind::kNone) {
+      continue;
+    }
+    DCHECK(catch_location == DexRegisterLocation::Kind::kInStack);
+
+    // Get vreg value from its current location.
+    uint32_t vreg_value;
+    VRegKind vreg_kind = ToVRegKind(throw_vreg_map.GetLocationKind(vreg,
+                                                                   number_of_vregs,
+                                                                   code_info,
+                                                                   encoding));
+    bool get_vreg_success = stack_visitor->GetVReg(stack_visitor->GetMethod(),
+                                                   vreg,
+                                                   vreg_kind,
+                                                   &vreg_value);
+    CHECK(get_vreg_success) << "VReg " << vreg << " was optimized out ("
+                            << "method=" << PrettyMethod(stack_visitor->GetMethod()) << ", "
+                            << "dex_pc=" << stack_visitor->GetDexPc() << ", "
+                            << "native_pc_offset=" << stack_visitor->GetNativePcOffset() << ")";
+
+    // Copy value to the catch phi's stack slot.
+    int32_t slot_offset = catch_vreg_map.GetStackOffsetInBytes(vreg,
+                                                               number_of_vregs,
+                                                               code_info,
+                                                               encoding);
+    ArtMethod** frame_top = stack_visitor->GetCurrentQuickFrame();
+    uint8_t* slot_address = reinterpret_cast<uint8_t*>(frame_top) + slot_offset;
+    uint32_t* slot_ptr = reinterpret_cast<uint32_t*>(slot_address);
+    *slot_ptr = vreg_value;
+  }
 }
 
 // Prepares deoptimization.
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 4db95a8..2e05c7e 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -49,11 +49,14 @@
   // Deoptimize the stack to the upcall. For every compiled frame, we create a "copy"
   // shadow frame that will be executed with the interpreter.
   void DeoptimizeStack() SHARED_REQUIRES(Locks::mutator_lock_);
-
   // Update the instrumentation stack by removing all methods that will be unwound
   // by the exception being thrown.
   void UpdateInstrumentationStack() SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Set up environment before delivering an exception to optimized code.
+  void SetCatchEnvironmentForOptimizedHandler(StackVisitor* stack_visitor)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
   // Long jump either to a catch handler or to the upcall.
   NO_RETURN void DoLongJump() SHARED_REQUIRES(Locks::mutator_lock_);
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 7c71e13..bbadb1e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -57,6 +57,7 @@
 #include "atomic.h"
 #include "base/arena_allocator.h"
 #include "base/dumpable.h"
+#include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker-inl.h"
 #include "compiler_callbacks.h"
@@ -129,6 +130,7 @@
 #include "thread_list.h"
 #include "trace.h"
 #include "transaction.h"
+#include "utils.h"
 #include "verifier/method_verifier.h"
 #include "well_known_classes.h"
 
diff --git a/runtime/stack.cc b/runtime/stack.cc
index a765a3f..d956f0e 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -325,6 +325,10 @@
 
 bool StackVisitor::GetRegisterIfAccessible(uint32_t reg, VRegKind kind, uint32_t* val) const {
   const bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+
+  // X86 float registers are 64-bit and the logic below does not apply.
+  DCHECK(!is_float || kRuntimeISA != InstructionSet::kX86);
+
   if (!IsAccessibleRegister(reg, is_float)) {
     return false;
   }
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 07b79b5..a15a081 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -1115,7 +1115,7 @@
     region_.StoreUnaligned<NumberOfStackMapsType>(kNumberOfStackMapsOffset, number_of_stack_maps);
   }
 
-  // Get the size all the stack maps of this CodeInfo object, in bytes.
+  // Get the size of all the stack maps of this CodeInfo object, in bytes.
   size_t GetStackMapsSize(const StackMapEncoding& encoding) const {
     return encoding.ComputeStackMapSize() * GetNumberOfStackMaps();
   }
@@ -1174,9 +1174,23 @@
     return StackMap();
   }
 
+  // Searches the stack map list backwards because catch stack maps are stored
+  // at the end.
+  StackMap GetCatchStackMapForDexPc(uint32_t dex_pc, const StackMapEncoding& encoding) const {
+    for (size_t i = GetNumberOfStackMaps(); i > 0; --i) {
+      StackMap stack_map = GetStackMapAt(i - 1, encoding);
+      if (stack_map.GetDexPc(encoding) == dex_pc) {
+        return stack_map;
+      }
+    }
+    return StackMap();
+  }
+
   StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset,
                                         const StackMapEncoding& encoding) const {
-    // TODO: stack maps are sorted by native pc, we can do a binary search.
+    // TODO: Safepoint stack maps are sorted by native_pc_offset but catch stack
+    //       maps are not. If we knew that the method does not have try/catch,
+    //       we could do binary search.
     for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
       StackMap stack_map = GetStackMapAt(i, encoding);
       if (stack_map.GetNativePcOffset(encoding) == native_pc_offset) {
diff --git a/runtime/utils.h b/runtime/utils.h
index 16835c2..3e61824 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -294,25 +294,6 @@
   buf->push_back((data >> 24) & 0xff);
 }
 
-// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
-struct FreeDelete {
-  // NOTE: Deleting a const object is valid but free() takes a non-const pointer.
-  void operator()(const void* ptr) const {
-    free(const_cast<void*>(ptr));
-  }
-};
-
-// Alias for std::unique_ptr<> that uses the C function free() to delete objects.
-template <typename T>
-using UniqueCPtr = std::unique_ptr<T, FreeDelete>;
-
-// C++14 from-the-future import (std::make_unique)
-// Invoke the constructor of 'T' with the provided args, and wrap the result in a unique ptr.
-template <typename T, typename ... Args>
-std::unique_ptr<T> MakeUnique(Args&& ... args) {
-  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
 inline bool TestBitmap(size_t idx, const uint8_t* bitmap) {
   return ((bitmap[idx / kBitsPerByte] >> (idx % kBitsPerByte)) & 0x01) != 0;
 }
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1ed6980..3d4f04c 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -22,6 +22,7 @@
 #include "art_method-inl.h"
 #include "base/logging.h"
 #include "base/mutex-inl.h"
+#include "base/stl_util.h"
 #include "base/time_utils.h"
 #include "class_linker.h"
 #include "compiler_callbacks.h"
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 5e661a5..ba694b7 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_VERIFIER_METHOD_VERIFIER_H_
 
 #include <memory>
+#include <sstream>
 #include <vector>
 
 #include "base/macros.h"
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index e14306c..bb756e9 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -17,6 +17,7 @@
 #include "reg_type_cache-inl.h"
 
 #include "base/casts.h"
+#include "base/stl_util.h"
 #include "class_linker-inl.h"
 #include "dex_file-inl.h"
 #include "mirror/class-inl.h"
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index 8319de6..93948a1 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -19,7 +19,6 @@
 
 #include "base/casts.h"
 #include "base/macros.h"
-#include "base/stl_util.h"
 #include "object_callbacks.h"
 #include "reg_type.h"
 #include "runtime.h"
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 767e1de..55a77ac 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -22,7 +22,9 @@
 #define CHECK_REGS_CONTAIN_REFS(dex_pc, abort_if_not_found, ...) do { \
   int t[] = {__VA_ARGS__}; \
   int t_size = sizeof(t) / sizeof(*t); \
-  uintptr_t native_quick_pc = m->ToNativeQuickPc(dex_pc, abort_if_not_found); \
+  uintptr_t native_quick_pc = m->ToNativeQuickPc(dex_pc, \
+                                                 /* is_catch_handler */ false, \
+                                                 abort_if_not_found); \
   if (native_quick_pc != UINTPTR_MAX) { \
     CheckReferences(t, t_size, m->NativeQuickPcOffset(native_quick_pc)); \
   } \
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 59722ad..78f8842 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -235,6 +235,7 @@
 
   return result ? JNI_TRUE : JNI_FALSE;
 #else
+  UNUSED(pid_int);
   return JNI_FALSE;
 #endif
 }
diff --git a/test/510-checker-try-catch/smali/RegisterAllocator.smali b/test/510-checker-try-catch/smali/RegisterAllocator.smali
new file mode 100644
index 0000000..fd3c84c
--- /dev/null
+++ b/test/510-checker-try-catch/smali/RegisterAllocator.smali
@@ -0,0 +1,94 @@
+# 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.
+
+.class public LRegisterAllocator;
+
+.super Ljava/lang/Object;
+
+# Test that catch phis are allocated to a stack slot, and that equivalent catch
+# phis are allocated to the same stack slot.
+
+## CHECK-START: int RegisterAllocator.testEquivalentCatchPhiSlot_Single(int, int, int) register (after)
+## CHECK-DAG:     Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}-><<SlotA1:\d+>>(sp)
+## CHECK-DAG:     Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}-><<SlotA2:\d+>>(sp)
+## CHECK-DAG:     Phi reg:1 is_catch_phi:true locations:{{\[.*\]}}-><<SlotB:\d+>>(sp)
+## CHECK-EVAL:    <<SlotA1>> == <<SlotA2>>
+## CHECK-EVAL:    <<SlotB>> != <<SlotA1>>
+
+.method public static testEquivalentCatchPhiSlot_Single(III)I
+  .registers 8
+
+  :try_start
+  const/high16 v0, 0x40000000 # float 2
+  move v1, p0
+  div-int/2addr p0, p1
+
+  const/high16 v0, 0x41000000 # float 8
+  move v1, p1
+  div-int/2addr p0, p2
+  goto :return
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  :catch_all
+  # 2x CatchPhi for v0, 1x for v1
+  if-eqz v1, :use_as_float
+
+  :use_as_int
+  goto :return
+
+  :use_as_float
+  float-to-int v0, v0
+
+  :return
+  return v0
+.end method
+
+# Test that wide catch phis are allocated to two stack slots.
+
+## CHECK-START: long RegisterAllocator.testEquivalentCatchPhiSlot_Wide(int, int, int) register (after)
+## CHECK-DAG:     Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}->2x<<SlotB1:\d+>>(sp)
+## CHECK-DAG:     Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}->2x<<SlotB2:\d+>>(sp)
+## CHECK-DAG:     Phi reg:2 is_catch_phi:true locations:{{\[.*\]}}-><<SlotA:\d+>>(sp)
+## CHECK-EVAL:    <<SlotB1>> == <<SlotB2>>
+## CHECK-EVAL:    abs(<<SlotA>> - <<SlotB1>>) >= 8
+
+.method public static testEquivalentCatchPhiSlot_Wide(III)J
+  .registers 8
+
+  :try_start
+  const-wide/high16 v0, 0x4000000000000000L # double 2
+  move v2, p0
+  div-int/2addr p0, p1
+
+  const-wide/high16 v0, 0x4100000000000000L # double 8
+  move v2, p1
+  div-int/2addr p0, p2
+  goto :return
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  :catch_all
+  # 2x CatchPhi for v0, 1x for v2
+  if-eqz v2, :use_as_double
+
+  :use_as_long
+  goto :return
+
+  :use_as_double
+  double-to-long v0, v0
+
+  :return
+  return-wide v0
+.end method
diff --git a/test/510-checker-try-catch/smali/Runtime.smali b/test/510-checker-try-catch/smali/Runtime.smali
new file mode 100644
index 0000000..19b43a3
--- /dev/null
+++ b/test/510-checker-try-catch/smali/Runtime.smali
@@ -0,0 +1,555 @@
+# 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.
+
+.class public LRuntime;
+.super Ljava/lang/Object;
+
+# The following tests all share the same structure, signature and return values:
+#  - foo(false, false):  normal path,         returns 42
+#  - foo(true, false):   exceptional path #1, returns 3
+#  - foo(false, true):   exceptional path #2, returns 8
+#  - foo(true, true):    undefined
+
+
+# Test register allocation of 32-bit core intervals crossing catch block positions.
+
+## CHECK-START: int Runtime.testUseAfterCatch_int(boolean, boolean) register (after)
+## CHECK-NOT:     Phi is_catch_phi:true
+
+.method public static testUseAfterCatch_int(ZZ)I
+  .registers 6
+
+  sget-object v0, LRuntime;->intArray:[I
+  const/4 v1, 0
+  aget v1, v0, v1
+  const/4 v2, 1
+  aget v2, v0, v2
+  const/4 v3, 2
+  aget v3, v0, v3
+
+  :try_start
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  return v3  # Normal path return.
+
+  :catch_all
+  if-eqz p0, :second_throw
+  return v1  # Exceptional path #1 return.
+
+  :second_throw
+  return v2  # Exceptional path #2 return.
+.end method
+
+
+# Test register allocation of 64-bit core intervals crossing catch block positions.
+
+# The sum of the low and high 32 bits treated as integers is returned to prove
+# that both vregs allocated correctly.
+
+## CHECK-START: int Runtime.testUseAfterCatch_long(boolean, boolean) register (after)
+## CHECK-NOT:     Phi is_catch_phi:true
+
+.method public static testUseAfterCatch_long(ZZ)I
+  .registers 10
+
+  sget-object v0, LRuntime;->longArray:[J
+  const/4 v1, 0
+  aget-wide v1, v0, v1
+  const/4 v3, 1
+  aget-wide v3, v0, v3
+  const/4 v5, 2
+  aget-wide v5, v0, v5
+
+  :try_start
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  const v0, 32
+  ushr-long v7, v5, v0
+  long-to-int v5, v5
+  long-to-int v7, v7
+  add-int/2addr v5, v7
+  return v5  # Normal path return.
+
+  :catch_all
+  const v0, 32
+  if-eqz p0, :second_throw
+
+  ushr-long v7, v1, v0
+  long-to-int v1, v1
+  long-to-int v7, v7
+  add-int/2addr v1, v7
+  return v1  # Exceptional path #1 return.
+
+  :second_throw
+  ushr-long v7, v3, v0
+  long-to-int v3, v3
+  long-to-int v7, v7
+  add-int/2addr v3, v7
+  return v3  # Exceptional path #2 return.
+.end method
+
+
+# Test register allocation of 32-bit floating-point intervals crossing catch block positions.
+
+## CHECK-START: int Runtime.testUseAfterCatch_float(boolean, boolean) register (after)
+## CHECK-NOT:     Phi is_catch_phi:true
+
+.method public static testUseAfterCatch_float(ZZ)I
+  .registers 6
+
+  sget-object v0, LRuntime;->floatArray:[F
+  const/4 v1, 0
+  aget v1, v0, v1
+  const/4 v2, 1
+  aget v2, v0, v2
+  const/4 v3, 2
+  aget v3, v0, v3
+
+  :try_start
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  float-to-int v3, v3
+  return v3  # Normal path return.
+
+  :catch_all
+  if-eqz p0, :second_throw
+  float-to-int v1, v1
+  return v1  # Exceptional path #1 return.
+
+  :second_throw
+  float-to-int v2, v2
+  return v2  # Exceptional path #2 return.
+.end method
+
+
+# Test register allocation of 64-bit floating-point intervals crossing catch block positions.
+
+## CHECK-START: int Runtime.testUseAfterCatch_double(boolean, boolean) register (after)
+## CHECK-NOT:     Phi is_catch_phi:true
+
+.method public static testUseAfterCatch_double(ZZ)I
+  .registers 10
+
+  sget-object v0, LRuntime;->doubleArray:[D
+  const/4 v1, 0
+  aget-wide v1, v0, v1
+  const/4 v3, 1
+  aget-wide v3, v0, v3
+  const/4 v5, 2
+  aget-wide v5, v0, v5
+
+  :try_start
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  double-to-int v5, v5
+  return v5  # Normal path return.
+
+  :catch_all
+  if-eqz p0, :second_throw
+  double-to-int v1, v1
+  return v1  # Exceptional path #1 return.
+
+  :second_throw
+  double-to-int v3, v3
+  return v3  # Exceptional path #2 return.
+.end method
+
+
+# Test catch-phi runtime support for constant values.
+
+# Register v0 holds different constants at two throwing instructions. Runtime is
+# expected to load them from stack map and copy to the catch phi's location.
+
+## CHECK-START: int Runtime.testCatchPhi_const(boolean, boolean) register (after)
+## CHECK-DAG:     <<Const3:i\d+>> IntConstant 3
+## CHECK-DAG:     <<Const8:i\d+>> IntConstant 8
+## CHECK-DAG:                     Phi [<<Const3>>,<<Const8>>] is_catch_phi:true
+
+.method public static testCatchPhi_const(ZZ)I
+  .registers 3
+
+  :try_start
+  const v0, 3
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+
+  const v0, 8
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  const v0, 42
+  return v0  # Normal path return.
+
+  :catch_all
+  return v0  # Exceptional path #1/#2 return.
+.end method
+
+
+# Test catch-phi runtime support for 32-bit values stored in core registers.
+
+# Register v0 holds different integer values at two throwing instructions.
+# Runtime is expected to find their location in the stack map and copy the value
+# to the location of the catch phi.
+
+## CHECK-START: int Runtime.testCatchPhi_int(boolean, boolean) register (after)
+## CHECK-DAG:     <<Val1:i\d+>> ArrayGet
+## CHECK-DAG:     <<Val2:i\d+>> ArrayGet
+## CHECK-DAG:                   Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
+
+.method public static testCatchPhi_int(ZZ)I
+  .registers 6
+
+  sget-object v0, LRuntime;->intArray:[I
+  const/4 v1, 0
+  aget v1, v0, v1
+  const/4 v2, 1
+  aget v2, v0, v2
+  const/4 v3, 2
+  aget v3, v0, v3
+
+  :try_start
+  move v0, v1  # Set catch phi value
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+
+  move v0, v2  # Set catch phi value
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  return v3  # Normal path return.
+
+  :catch_all
+  return v0  # Exceptional path #1/#2 return.
+.end method
+
+
+# Test catch-phi runtime support for 64-bit values stored in core registers.
+
+# Register pair (v0, v1) holds different long values at two throwing instructions.
+# Runtime is expected to find their location in the stack map and copy the value
+# to the location of the catch phi. The sum of the low and high 32 bits treated
+# as integers is returned to prove that both vregs were copied.
+
+# Note: values will be spilled on x86 because of too few callee-save core registers.
+
+## CHECK-START: int Runtime.testCatchPhi_long(boolean, boolean) register (after)
+## CHECK-DAG:     <<Val1:j\d+>> ArrayGet
+## CHECK-DAG:     <<Val2:j\d+>> ArrayGet
+## CHECK-DAG:                   Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
+
+.method public static testCatchPhi_long(ZZ)I
+  .registers 10
+
+  sget-object v0, LRuntime;->longArray:[J
+  const/4 v2, 0
+  aget-wide v2, v0, v2
+  const/4 v4, 1
+  aget-wide v4, v0, v4
+  const/4 v6, 2
+  aget-wide v6, v0, v6
+
+  :try_start
+  move-wide v0, v2  # Set catch phi value
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+
+  move-wide v0, v4  # Set catch phi value
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  const v2, 32
+  ushr-long v2, v6, v2
+  long-to-int v2, v2
+  long-to-int v6, v6
+  add-int/2addr v6, v2
+  return v6  # Normal path return.
+
+  :catch_all
+  const v2, 32
+  ushr-long v2, v0, v2
+  long-to-int v2, v2
+  long-to-int v0, v0
+  add-int/2addr v0, v2
+  return v0  # Exceptional path #1/#2 return.
+.end method
+
+
+# Test catch-phi runtime support for 32-bit values stored in FPU registers.
+
+# Register v0 holds different float values at two throwing instructions. Runtime
+# is expected to find their location in the stack map and copy the value to the
+# location of the catch phi. The value is converted to int and returned.
+
+# Note: values will be spilled on x86 as there are no callee-save FPU registers.
+
+## CHECK-START: int Runtime.testCatchPhi_float(boolean, boolean) register (after)
+## CHECK-DAG:     <<Val1:f\d+>> ArrayGet
+## CHECK-DAG:     <<Val2:f\d+>> ArrayGet
+## CHECK-DAG:                   Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
+
+.method public static testCatchPhi_float(ZZ)I
+  .registers 6
+
+  sget-object v0, LRuntime;->floatArray:[F
+  const/4 v1, 0
+  aget v1, v0, v1
+  const/4 v2, 1
+  aget v2, v0, v2
+  const/4 v3, 2
+  aget v3, v0, v3
+
+  :try_start
+  move v0, v1  # Set catch phi value
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+
+  move v0, v2  # Set catch phi value
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  float-to-int v3, v3
+  return v3  # Normal path return.
+
+  :catch_all
+  float-to-int v0, v0
+  return v0  # Exceptional path #1/#2 return.
+.end method
+
+
+# Test catch-phi runtime support for 64-bit values stored in FPU registers.
+
+# Register pair (v0, v1) holds different double values at two throwing instructions.
+# Runtime is expected to find their location in the stack map and copy the value
+# to the location of the catch phi. The value is converted to int and returned.
+# Values were chosen so that all 64 bits are used.
+
+# Note: values will be spilled on x86 as there are no callee-save FPU registers.
+
+## CHECK-START: int Runtime.testCatchPhi_double(boolean, boolean) register (after)
+## CHECK-DAG:     <<Val1:d\d+>> ArrayGet
+## CHECK-DAG:     <<Val2:d\d+>> ArrayGet
+## CHECK-DAG:                   Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
+
+.method public static testCatchPhi_double(ZZ)I
+  .registers 10
+
+  sget-object v0, LRuntime;->doubleArray:[D
+  const/4 v2, 0
+  aget-wide v2, v0, v2
+  const/4 v4, 1
+  aget-wide v4, v0, v4
+  const/4 v6, 2
+  aget-wide v6, v0, v6
+
+  :try_start
+  move-wide v0, v2  # Set catch phi value
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+
+  move-wide v0, v4  # Set catch phi value
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  double-to-int v6, v6
+  return v6
+
+  :catch_all
+  double-to-int v0, v0
+  return v0
+.end method
+
+# Test catch-phi runtime support for 32-bit values stored on the stack.
+
+# Register v0 holds different integer values at two throwing instructions.
+# These values were forced to spill by an always-throwing try/catch after their
+# definition. Runtime is expected to find their location in the stack map and
+# copy the value to the location of the catch phi. The value is then returned.
+
+## CHECK-START: int Runtime.testCatchPhi_singleSlot(boolean, boolean) register (after)
+## CHECK:         <<Val1:i\d+>> ArrayGet
+## CHECK-NEXT:                  ParallelMove moves:[{{.*->}}{{\d+}}(sp)]
+## CHECK:         <<Val2:i\d+>> ArrayGet
+## CHECK-NEXT:                  ParallelMove moves:[{{.*->}}{{\d+}}(sp)]
+## CHECK:                       Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
+
+.method public static testCatchPhi_singleSlot(ZZ)I
+  .registers 6
+
+  sget-object v0, LRuntime;->intArray:[I
+  const/4 v1, 0
+  aget v1, v0, v1
+  const/4 v2, 1
+  aget v2, v0, v2
+  const/4 v3, 2
+  aget v3, v0, v3
+
+  # Insert a try/catch to force v1,v2,v3 to spill.
+  :try_start_spill
+  const/4 v0, 1
+  invoke-static {v0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end_spill
+  .catchall {:try_start_spill .. :try_end_spill} :catch_all_spill
+  return v0         # Unreachable
+  :catch_all_spill  # Catch and continue
+
+  :try_start
+  move v0, v1  # Set catch phi value
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+
+  move v0, v2  # Set catch phi value
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  return v3  # Normal path return.
+
+  :catch_all
+  return v0  # Exceptional path #1/#2 return.
+.end method
+
+# Test catch-phi runtime support for 64-bit values stored on the stack.
+
+# Register pair (v0, v1) holds different double values at two throwing instructions.
+# These values were forced to spill by an always-throwing try/catch after their
+# definition. Runtime is expected to find their location in the stack map and
+# copy the value to the location of the catch phi. The value is converted to int
+# and returned. Values were chosen so that all 64 bits are used.
+
+## CHECK-START: int Runtime.testCatchPhi_doubleSlot(boolean, boolean) register (after)
+## CHECK:         <<Val1:d\d+>> ArrayGet
+## CHECK-NEXT:                  ParallelMove moves:[{{.*->}}2x{{\d+}}(sp)]
+## CHECK:         <<Val2:d\d+>> ArrayGet
+## CHECK-NEXT:                  ParallelMove moves:[{{.*->}}2x{{\d+}}(sp)]
+## CHECK:                       Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
+
+.method public static testCatchPhi_doubleSlot(ZZ)I
+  .registers 10
+
+  sget-object v0, LRuntime;->doubleArray:[D
+  const/4 v2, 0
+  aget-wide v2, v0, v2
+  const/4 v4, 1
+  aget-wide v4, v0, v4
+  const/4 v6, 2
+  aget-wide v6, v0, v6
+
+  # Insert a try/catch to force (v2, v3), (v4, v5), (v6, v7) to spill.
+  :try_start_spill
+  const/4 v0, 1
+  invoke-static {v0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end_spill
+  .catchall {:try_start_spill .. :try_end_spill} :catch_all_spill
+  return v0         # Unreachable
+  :catch_all_spill  # Catch and continue
+
+  :try_start
+  move-wide v0, v2  # Set catch phi value
+  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+
+  move-wide v0, v4  # Set catch phi value
+  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  double-to-int v6, v6
+  return v6  # Normal path return.
+
+  :catch_all
+  double-to-int v0, v0
+  return v0  # Exceptional path #1/#2 return.
+.end method
+
+
+
+# Helper methods and initialization.
+
+.method public static $noinline$ThrowIfTrue(Z)V
+  .registers 2
+  if-nez p0, :throw
+  return-void
+
+  :throw
+  new-instance v0, Ljava/lang/Exception;
+  invoke-direct {v0}, Ljava/lang/Exception;-><init>()V
+  throw v0
+.end method
+
+.method public static constructor <clinit>()V
+  .registers 2
+
+  const/4 v1, 4
+
+  new-array v0, v1, [I
+  fill-array-data v0, :array_int
+  sput-object v0, LRuntime;->intArray:[I
+
+  new-array v0, v1, [J
+  fill-array-data v0, :array_long
+  sput-object v0, LRuntime;->longArray:[J
+
+  new-array v0, v1, [F
+  fill-array-data v0, :array_float
+  sput-object v0, LRuntime;->floatArray:[F
+
+  new-array v0, v1, [D
+  fill-array-data v0, :array_double
+  sput-object v0, LRuntime;->doubleArray:[D
+
+  return-void
+
+:array_int
+.array-data 4
+  0x03  # int 3
+  0x08  # int 8
+  0x2a  # int 42
+.end array-data
+
+:array_long
+.array-data 8
+  0x0000000100000002L # long (1 << 32) + 2
+  0x0000000500000003L # long (5 << 32) + 3
+  0x0000001e0000000cL # long (30 << 32) + 12
+.end array-data
+
+:array_float
+.array-data 4
+  0x40400000  # float 3
+  0x41000000  # float 8
+  0x42280000  # float 42
+.end array-data
+
+:array_double
+.array-data 8
+  0x400b333333333333L  # double 3.4
+  0x4020cccccccccccdL  # double 8.4
+  0x4045333333333333L  # double 42.4
+.end array-data
+.end method
+
+.field public static intArray:[I
+.field public static longArray:[J
+.field public static floatArray:[F
+.field public static doubleArray:[D
diff --git a/test/510-checker-try-catch/src/Main.java b/test/510-checker-try-catch/src/Main.java
index ae78ba0..25cdc0e 100644
--- a/test/510-checker-try-catch/src/Main.java
+++ b/test/510-checker-try-catch/src/Main.java
@@ -14,10 +14,55 @@
  * limitations under the License.
  */
 
+import java.lang.reflect.Method;
+
 public class Main {
 
   // Workaround for b/18051191.
   class InnerClass {}
 
-  public static void main(String[] args) {}
+  public enum TestPath {
+    ExceptionalFlow1(true, false, 3),
+    ExceptionalFlow2(false, true, 8),
+    NormalFlow(false, false, 42);
+
+    TestPath(boolean arg1, boolean arg2, int expected) {
+      this.arg1 = arg1;
+      this.arg2 = arg2;
+      this.expected = expected;
+    }
+
+    public boolean arg1;
+    public boolean arg2;
+    public int expected;
+  }
+
+  public static void testMethod(String method) throws Exception {
+    Class<?> c = Class.forName("Runtime");
+    Method m = c.getMethod(method, new Class[] { boolean.class, boolean.class });
+
+    for (TestPath path : TestPath.values()) {
+      Object[] arguments = new Object[] { path.arg1, path.arg2 };
+      int actual = (Integer) m.invoke(null, arguments);
+
+      if (actual != path.expected) {
+        throw new Error("Method: \"" + method + "\", path: " + path + ", " +
+                        "expected: " + path.expected + ", actual: " + actual);
+      }
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    testMethod("testUseAfterCatch_int");
+    testMethod("testUseAfterCatch_long");
+    testMethod("testUseAfterCatch_float");
+    testMethod("testUseAfterCatch_double");
+    testMethod("testCatchPhi_const");
+    testMethod("testCatchPhi_int");
+    testMethod("testCatchPhi_long");
+    testMethod("testCatchPhi_float");
+    testMethod("testCatchPhi_double");
+    testMethod("testCatchPhi_singleSlot");
+    testMethod("testCatchPhi_doubleSlot");
+  }
 }
diff --git a/test/530-checker-regression-reftype-final/expected.txt b/test/530-checker-regression-reftype-final/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/530-checker-regression-reftype-final/expected.txt
diff --git a/test/530-checker-regression-reftype-final/info.txt b/test/530-checker-regression-reftype-final/info.txt
new file mode 100644
index 0000000..07789d6
--- /dev/null
+++ b/test/530-checker-regression-reftype-final/info.txt
@@ -0,0 +1 @@
+Regression test for optimizing that used assume that array types are always exact.
diff --git a/test/530-checker-regression-reftype-final/smali/TestCase.smali b/test/530-checker-regression-reftype-final/smali/TestCase.smali
new file mode 100644
index 0000000..8fd7bb7
--- /dev/null
+++ b/test/530-checker-regression-reftype-final/smali/TestCase.smali
@@ -0,0 +1,59 @@
+# 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.
+
+.class public LTestCase;
+.super Ljava/lang/Object;
+
+# Inliner used to assign exact type to the artificial multiple-return phi if the
+# class type was final which does not hold for arrays.
+
+# The type information is only used by recursive calls to the inliner and is
+# overwritten by the next pass of reference type propagation. Since we do not
+# inline any methods from array classes, this bug cannot be triggered and we
+# verify it using Checker.
+
+## CHECK-START: void TestCase.testInliner() reference_type_propagation_after_inlining (before)
+## CHECK-DAG:             CheckCast [<<Phi:l\d+>>,{{l\d+}}]
+## CHECK-DAG:    <<Phi>>  Phi klass:java.lang.Object[] exact:false
+
+.method public static testInliner()V
+  .registers 3
+
+  invoke-static {}, Ljava/lang/System;->nanoTime()J
+  move-result-wide v0
+  long-to-int v0, v0
+
+  invoke-static {v0}, LTestCase;->$inline$getArray(I)[Ljava/lang/Object;
+  move-result-object v0
+
+  check-cast v0, [LMain$MyClassA;
+  return-void
+
+.end method
+
+.method public static $inline$getArray(I)[Ljava/lang/Object;
+  .registers 2
+  if-eqz p0, :else
+
+  :then
+  const/4 v0, 2
+  new-array v0, v0, [LMain$MyClassA;
+  return-object v0
+
+  :else
+  const/4 v0, 3
+  new-array v0, v0, [LMain$MyClassB;
+  return-object v0
+
+.end method
diff --git a/test/530-checker-regression-reftype-final/src/Main.java b/test/530-checker-regression-reftype-final/src/Main.java
new file mode 100644
index 0000000..f86b515
--- /dev/null
+++ b/test/530-checker-regression-reftype-final/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main  {
+
+  class MyClassA {}
+  class MyClassB extends MyClassA {}
+
+  public static void main(String[] args) throws Exception {
+    testReferenceTypePropagation();
+    invokeTestInliner();
+  }
+
+  // Reference type propagation (RTP) used to assume that if a class is final,
+  // then the type must be exact. This does not hold for arrays which are always
+  // final, i.e. not extendable, but may be assigned to from values of the
+  // components type subclasses.
+
+  public static void testReferenceTypePropagation() throws Exception {
+    boolean expectTrue;
+
+    // Bug #1: RTP would set the type of `array` to exact Object[]. Instruction
+    // simplifier would then simplify the instanceof to `false`.
+    Object[] array = $noinline$getArray();
+    expectTrue = array instanceof MyClassA[];
+    if (!expectTrue) {
+      throw new Exception("Incorrect type check.");
+    }
+
+    // Bug #2: This is the true-branch of the instanceof above. The bound type
+    // for `array` would be again set to exact MyClassA[] and incorrectly
+    // simplify the second instanceof to `false`.
+    expectTrue = array instanceof MyClassB[];
+    if (!expectTrue) {
+      throw new Exception("Incorrect type bound.");
+    }
+  }
+
+  public static void invokeTestInliner() throws Exception {
+    Class<?> c = Class.forName("TestCase");
+    Method m = c.getMethod("testInliner");
+    m.invoke(null);
+  }
+
+  public static Object[] $noinline$getArray() {
+    if (doThrow) throw new Error();
+    return new MyClassB[2];
+  }
+
+  static boolean doThrow = false;
+}
diff --git a/test/998-scoped-primitive-array/check b/test/998-scoped-primitive-array/check
deleted file mode 100755
index 842bdc6..0000000
--- a/test/998-scoped-primitive-array/check
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Check that the string "error" isn't present
-if grep error "$2"; then
-    exit 1
-else
-    exit 0
-fi
diff --git a/test/998-scoped-primitive-array/expected.txt b/test/998-scoped-primitive-array/expected.txt
deleted file mode 100644
index a965a70..0000000
--- a/test/998-scoped-primitive-array/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-Done
diff --git a/test/998-scoped-primitive-array/scoped_primitive_array.cc b/test/998-scoped-primitive-array/scoped_primitive_array.cc
deleted file mode 100644
index c224a06..0000000
--- a/test/998-scoped-primitive-array/scoped_primitive_array.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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 "jni.h"
-#include "ScopedPrimitiveArray.h"
-
-extern "C" JNIEXPORT jlong JNICALL Java_Main_measureByteArray(JNIEnv* env,
-                                                              jclass,
-                                                              jlong reps,
-                                                              jbyteArray arr) {
-  jlong ret = 0;
-  for (jlong i = 0; i < reps; ++i) {
-    ScopedByteArrayRO sc(env, arr);
-    ret += sc[0] + sc[sc.size() - 1];
-  }
-  return ret;
-}
-
-extern "C" JNIEXPORT jlong JNICALL Java_Main_measureShortArray(JNIEnv* env,
-                                                               jclass,
-                                                               jlong reps,
-                                                               jshortArray arr) {
-  jlong ret = 0;
-  for (jlong i = 0; i < reps; ++i) {
-    ScopedShortArrayRO sc(env, arr);
-    ret += sc[0] + sc[sc.size() - 1];
-  }
-  return ret;
-}
-
-extern "C" JNIEXPORT jlong JNICALL Java_Main_measureIntArray(JNIEnv* env,
-                                                             jclass,
-                                                             jlong reps,
-                                                             jintArray arr) {
-  jlong ret = 0;
-  for (jlong i = 0; i < reps; ++i) {
-    ScopedIntArrayRO sc(env, arr);
-    ret += sc[0] + sc[sc.size() - 1];
-  }
-  return ret;
-}
-
-extern "C" JNIEXPORT jlong JNICALL Java_Main_measureLongArray(JNIEnv* env,
-                                                              jclass,
-                                                              jlong reps,
-                                                              jlongArray arr) {
-  jlong ret = 0;
-  for (jlong i = 0; i < reps; ++i) {
-    ScopedLongArrayRO sc(env, arr);
-    ret += sc[0] + sc[sc.size() - 1];
-  }
-  return ret;
-}
diff --git a/test/998-scoped-primitive-array/src/Main.java b/test/998-scoped-primitive-array/src/Main.java
deleted file mode 100644
index 630e0dc..0000000
--- a/test/998-scoped-primitive-array/src/Main.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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 {
-  public Main() {}
-
-  // Measure adds the first and last element of the array by using ScopedPrimitiveArray.
-  static native long measureByteArray(long reps, byte[] arr);
-  static native long measureShortArray(long reps, short[] arr);
-  static native long measureIntArray(long reps, int[] arr);
-  static native long measureLongArray(long reps, long[] arr);
-
-  static void checkEq(long expected, long value) {
-    if (expected != value) {
-      System.out.println("error: Expected " + expected + " but got " + value);
-    }
-  }
-
-  static void runPerfTest(long reps) {
-    for (int length = 1; length <= 8192; length *= 8) {
-      byte[] bytes = new byte[length];
-      bytes[0] = 1;
-      bytes[length - 1] = 2;
-      short[] shorts = new short[length];
-      shorts[0] = 1;
-      shorts[length - 1] = 2;
-      int[] ints = new int[length];
-      ints[0] = 1;
-      ints[length - 1] = 2;
-      long[] longs = new long[length];
-      longs[0] = 1;
-      longs[length - 1] = 2;
-      long value = 0;
-      long elapsed = 0;
-      long start = 0;
-
-      start = System.nanoTime();
-      value = measureByteArray(reps, bytes);
-      elapsed = System.nanoTime() - start;
-      System.out.println("Byte length=" + length + " ns/op=" + (double) elapsed / reps);
-      checkEq(value, reps * (long) (bytes[0] + bytes[length - 1]));
-
-      start = System.nanoTime();
-      value = measureShortArray(reps, shorts);
-      elapsed = System.nanoTime() - start;
-      System.out.println("Short length=" + length + " ns/op=" + (double) elapsed / reps);
-      checkEq(value, reps * (long) (shorts[0] + shorts[length - 1]));
-
-      start = System.nanoTime();
-      value = measureIntArray(reps, ints);
-      elapsed = System.nanoTime() - start;
-      System.out.println("Int length=" + length + " ns/op=" + (double) elapsed / reps);
-      checkEq(value, reps * (ints[0] + ints[length - 1]));
-
-      start = System.nanoTime();
-      value = measureLongArray(reps, longs);
-      elapsed = System.nanoTime() - start;
-      System.out.println("Long length=" + length + " ns/op=" + (double) elapsed / reps);
-      checkEq(value, reps * (longs[0] + longs[length - 1]));
-    }
-  }
-
-  public static void main(String[] args) {
-    System.loadLibrary(args[0]);
-    long iterations = 2000000;
-    if (args.length > 1) {
-      iterations = Long.parseLong(args[1], 10);
-    }
-    runPerfTest(iterations);
-    System.out.println("Done");
-  }
-}
diff --git a/test/999-jni-perf/check b/test/999-jni-perf/check
deleted file mode 100755
index ffbb8cf..0000000
--- a/test/999-jni-perf/check
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Only compare the last line.
-tail -n 1 "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
\ No newline at end of file
diff --git a/test/999-jni-perf/expected.txt b/test/999-jni-perf/expected.txt
deleted file mode 100644
index a965a70..0000000
--- a/test/999-jni-perf/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-Done
diff --git a/test/999-jni-perf/src/Main.java b/test/999-jni-perf/src/Main.java
deleted file mode 100644
index 032e700..0000000
--- a/test/999-jni-perf/src/Main.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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 {
-  public Main() {
-  }
-
-  private static final String MSG = "ABCDE";
-
-  native int perfJniEmptyCall();
-  native int perfSOACall();
-  native int perfSOAUncheckedCall();
-
-  int runPerfTest(long N) {
-    long start = System.nanoTime();
-    for (long i = 0; i < N; i++) {
-      char c = MSG.charAt(2);
-    }
-    long elapse = System.nanoTime() - start;
-    System.out.println("Fast JNI (charAt): " + (double)elapse / N);
-
-    start = System.nanoTime();
-    for (long i = 0; i < N; i++) {
-      perfJniEmptyCall();
-    }
-    elapse = System.nanoTime() - start;
-    System.out.println("Empty call: " + (double)elapse / N);
-
-    start = System.nanoTime();
-    for (long i = 0; i < N; i++) {
-      perfSOACall();
-    }
-    elapse = System.nanoTime() - start;
-    System.out.println("SOA call: " + (double)elapse / N);
-
-    start = System.nanoTime();
-    for (long i = 0; i < N; i++) {
-      perfSOAUncheckedCall();
-    }
-    elapse = System.nanoTime() - start;
-    System.out.println("SOA unchecked call: " + (double)elapse / N);
-
-    return 0;
-  }
-
-  public static void main(String[] args) {
-    System.loadLibrary(args[0]);
-    long iterations = 1000000;
-    if (args.length > 1) {
-      iterations = Long.parseLong(args[1], 10);
-    }
-    Main m = new Main();
-    m.runPerfTest(iterations);
-    System.out.println("Done");
-  }
-}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index af945fb..7f05a04 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -38,9 +38,7 @@
   457-regs/regs_jni.cc \
   461-get-reference-vreg/get_reference_vreg_jni.cc \
   466-get-live-vreg/get_live_vreg_jni.cc \
-  497-inlining-and-class-loader/clear_dex_cache.cc \
-  998-scoped-primitive-array/scoped_primitive_array.cc \
-  999-jni-perf/perf-jni.cc
+  497-inlining-and-class-loader/clear_dex_cache.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index e28de09..a84365a 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -32,6 +32,11 @@
   exit 1
 fi
 
+emulator="no"
+if [ "$ANDROID_SERIAL" = "emulator-5554" ]; then
+  emulator="yes"
+fi
+
 # Packages that currently work correctly with the expectation files.
 working_packages=("dalvik.system"
                   "libcore.icu"
@@ -81,10 +86,12 @@
     # Remove the --debug from the arguments.
     vogar_args=${vogar_args/$1}
     vogar_args="$vogar_args --vm-arg -XXlib:libartd.so"
-    # Increase the timeout, as vogar cannot set individual test
-    # timeout when being asked to run packages, and some tests go above
-    # the default timeout.
-    vogar_args="$vogar_args --timeout 240"
+    if [ "$emulator" = "no" ]; then
+      # Increase the timeout, as vogar cannot set individual test
+      # timeout when being asked to run packages, and some tests go above
+      # the default timeout.
+      vogar_args="$vogar_args --timeout 240"
+    fi
     shift
   elif [[ "$1" == "" ]]; then
     break
@@ -93,6 +100,11 @@
   fi
 done
 
+if [ "$emulator" = "yes" ]; then
+  # Be very patient with the emulator.
+  vogar_args="$vogar_args --timeout 480"
+fi
+
 # Run the tests using vogar.
 echo "Running tests for the following test packages:"
 echo ${working_packages[@]} | tr " " "\n"