Merge "Make ART compile with GCC -O0 again."
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index fce23bc..fba0863 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -143,7 +143,7 @@
 class NullCheckElimination : public PassME {
  public:
   NullCheckElimination()
-    : PassME("NCE", kRepeatingTopologicalSortTraversal, "3_post_nce_cfg") {
+    : PassME("NCE", kRepeatingPreOrderDFSTraversal, "3_post_nce_cfg") {
   }
 
   bool Gate(const PassDataHolder* data) const {
@@ -195,7 +195,7 @@
 class ClassInitCheckElimination : public PassME {
  public:
   ClassInitCheckElimination()
-    : PassME("ClInitCheckElimination", kLoopRepeatingTopologicalSortTraversal) {
+    : PassME("ClInitCheckElimination", kRepeatingPreOrderDFSTraversal) {
   }
 
   bool Gate(const PassDataHolder* data) const {
@@ -271,7 +271,8 @@
     DCHECK(data != nullptr);
     CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
     DCHECK(c_unit != nullptr);
-    return ((c_unit->disable_opt & (1 << kSuppressExceptionEdges)) != 0);
+    return c_unit->mir_graph->HasTryCatchBlocks() ||
+        ((c_unit->disable_opt & (1 << kSuppressExceptionEdges)) != 0);
   }
 
   bool Worker(PassDataHolder* data) const;
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index e4003bf..78da420 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -311,7 +311,8 @@
   kMIRCallee,                         // Instruction is inlined from callee.
   kMIRIgnoreSuspendCheck,
   kMIRDup,
-  kMIRMark,                           // Temporary node mark.
+  kMIRMark,                           // Temporary node mark can be used by
+                                      // opt passes for their private needs.
   kMIRStoreNonTemporal,
   kMIRLastMIRFlag,
 };
diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc
index eb98916..0fb5e48 100644
--- a/compiler/dex/local_value_numbering.cc
+++ b/compiler/dex/local_value_numbering.cc
@@ -1448,6 +1448,10 @@
       }
       break;
 
+    case kMirOpNullCheck:
+      HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0]));
+      break;
+
     case Instruction::INVOKE_DIRECT:
     case Instruction::INVOKE_DIRECT_RANGE:
     case Instruction::INVOKE_VIRTUAL:
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index 51b6709..0a6924c 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -118,10 +118,10 @@
   DF_DA | DF_REF_A | DF_NON_NULL_DST,
 
   // 1D MONITOR_ENTER vAA
-  DF_UA | DF_NULL_CHK_0 | DF_REF_A,
+  DF_UA | DF_NULL_CHK_A | DF_REF_A,
 
   // 1E MONITOR_EXIT vAA
-  DF_UA | DF_NULL_CHK_0 | DF_REF_A,
+  DF_UA | DF_NULL_CHK_A | DF_REF_A,
 
   // 1F CHK_CAST vAA, type@BBBB
   DF_UA | DF_REF_A | DF_UMS,
@@ -130,7 +130,7 @@
   DF_DA | DF_UB | DF_CORE_A | DF_REF_B | DF_UMS,
 
   // 21 ARRAY_LENGTH vA, vB
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_CORE_A | DF_REF_B,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_CORE_A | DF_REF_B,
 
   // 22 NEW_INSTANCE vAA, type@BBBB
   DF_DA | DF_NON_NULL_DST | DF_REF_A | DF_UMS,
@@ -235,88 +235,88 @@
   DF_NOP,
 
   // 44 AGET vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 45 AGET_WIDE vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 46 AGET_OBJECT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 47 AGET_BOOLEAN vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 48 AGET_BYTE vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 49 AGET_CHAR vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4A AGET_SHORT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4B APUT vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4C APUT_WIDE vAA, vBB, vCC
-  DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_2 | DF_RANGE_CHK_3 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4D APUT_OBJECT vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4E APUT_BOOLEAN vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4F APUT_BYTE vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 50 APUT_CHAR vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 51 APUT_SHORT vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 52 IGET vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 53 IGET_WIDE vA, vB, field@CCCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 54 IGET_OBJECT vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 55 IGET_BOOLEAN vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 56 IGET_BYTE vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 57 IGET_CHAR vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 58 IGET_SHORT vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 59 IPUT vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5A IPUT_WIDE vA, vB, field@CCCC
-  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5B IPUT_OBJECT vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5C IPUT_BOOLEAN vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5D IPUT_BYTE vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5E IPUT_CHAR vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5F IPUT_SHORT vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 60 SGET vAA, field@BBBB
   DF_DA | DF_SFIELD | DF_UMS,
@@ -712,10 +712,10 @@
   DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
 
   // E3 IGET_VOLATILE
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // E4 IPUT_VOLATILE
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // E5 SGET_VOLATILE
   DF_DA | DF_SFIELD | DF_UMS,
@@ -724,13 +724,13 @@
   DF_UA | DF_SFIELD | DF_UMS,
 
   // E7 IGET_OBJECT_VOLATILE
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // E8 IGET_WIDE_VOLATILE
-  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // E9 IPUT_WIDE_VOLATILE
-  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // EA SGET_WIDE_VOLATILE
   DF_DA | DF_A_WIDE | DF_SFIELD | DF_UMS,
@@ -751,28 +751,28 @@
   DF_FORMAT_3RC,
 
   // F0 INVOKE_OBJECT_INIT_RANGE
-  DF_NOP | DF_NULL_CHK_0,
+  DF_NOP,
 
   // F1 RETURN_VOID_BARRIER
   DF_NOP,
 
   // F2 IGET_QUICK
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F3 IGET_WIDE_QUICK
-  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN,
+  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F4 IGET_OBJECT_QUICK
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F5 IPUT_QUICK
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F6 IPUT_WIDE_QUICK
-  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_IFIELD | DF_LVN,
+  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F7 IPUT_OBJECT_QUICK
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F8 INVOKE_VIRTUAL_QUICK
   DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
@@ -787,7 +787,7 @@
   DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
 
   // FC IPUT_OBJECT_VOLATILE
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // FD SGET_OBJECT_VOLATILE
   DF_DA | DF_REF_A | DF_SFIELD | DF_UMS,
@@ -824,7 +824,7 @@
   DF_NOP,
 
   // 108 MIR_NULL_CHECK
-  DF_UA | DF_REF_A | DF_NULL_CHK_0 | DF_LVN,
+  DF_UA | DF_REF_A | DF_NULL_CHK_A | DF_LVN,
 
   // 109 MIR_RANGE_CHECK
   0,
@@ -893,10 +893,10 @@
   0,
 
   // 11F MirOpPackedArrayGet
-  DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 120 MirOpPackedArrayPut
-  DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 };
 
 /* Return the base virtual register for a SSA name */
@@ -1403,7 +1403,7 @@
       GetBlockName(bb, block_name1);
       GetBlockName(pred_bb, block_name2);
       DumpCFG("/sdcard/cfg/", false);
-      LOG(FATAL) << "Successor " << block_name1 << "not found from "
+      LOG(FATAL) << "Successor " << block_name1 << " not found from "
                  << block_name2;
     }
   }
diff --git a/compiler/dex/mir_field_info.h b/compiler/dex/mir_field_info.h
index 9745c41..1842a16 100644
--- a/compiler/dex/mir_field_info.h
+++ b/compiler/dex/mir_field_info.h
@@ -137,6 +137,7 @@
   // The member offset of the field, 0u if unresolved.
   MemberOffset field_offset_;
 
+  friend class NullCheckEliminationTest;
   friend class GlobalValueNumberingTest;
   friend class LocalValueNumberingTest;
 };
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index f0c9858..8dded79 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -86,6 +86,7 @@
       raw_use_counts_(arena->Adapter()),
       num_reachable_blocks_(0),
       max_num_reachable_blocks_(0),
+      dfs_orders_up_to_date_(false),
       dfs_order_(arena->Adapter(kArenaAllocDfsPreOrder)),
       dfs_post_order_(arena->Adapter(kArenaAllocDfsPostOrder)),
       dom_post_order_traversal_(arena->Adapter(kArenaAllocDomPostOrder)),
@@ -2224,7 +2225,7 @@
   }
 }
 
-void BasicBlock::Hide(CompilationUnit* c_unit) {
+void BasicBlock::Hide(MIRGraph* mir_graph) {
   // First lets make it a dalvik bytecode block so it doesn't have any special meaning.
   block_type = kDalvikByteCode;
 
@@ -2239,7 +2240,6 @@
   first_mir_insn = nullptr;
   last_mir_insn = nullptr;
 
-  MIRGraph* mir_graph = c_unit->mir_graph.get();
   for (BasicBlockId pred_id : predecessors) {
     BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id);
     DCHECK(pred_bb != nullptr);
@@ -2262,6 +2262,48 @@
   successor_block_list_type = kNotUsed;
 }
 
+/*
+ * Kill an unreachable block and all blocks that become unreachable by killing this one.
+ */
+void BasicBlock::KillUnreachable(MIRGraph* mir_graph) {
+  DCHECK(predecessors.empty());  // Unreachable.
+
+  // Mark as dead and hidden.
+  block_type = kDead;
+  hidden = true;
+
+  // Detach it from its MIRs so we don't generate code for them. Also detached MIRs
+  // are updated to know that they no longer have a parent.
+  for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) {
+    mir->bb = NullBasicBlockId;
+  }
+  first_mir_insn = nullptr;
+  last_mir_insn = nullptr;
+
+  data_flow_info = nullptr;
+
+  // Erase this bb from all children's predecessors and kill unreachable children.
+  ChildBlockIterator iter(this, mir_graph);
+  for (BasicBlock* succ_bb = iter.Next(); succ_bb != nullptr; succ_bb = iter.Next()) {
+    succ_bb->ErasePredecessor(id);
+    if (succ_bb->predecessors.empty()) {
+      succ_bb->KillUnreachable(mir_graph);
+    }
+  }
+
+  // Remove links to children.
+  fall_through = NullBasicBlockId;
+  taken = NullBasicBlockId;
+  successor_block_list_type = kNotUsed;
+
+  if (kIsDebugBuild) {
+    if (catch_entry) {
+      DCHECK_EQ(mir_graph->catches_.count(start_offset), 1u);
+      mir_graph->catches_.erase(start_offset);
+    }
+  }
+}
+
 bool BasicBlock::IsSSALiveOut(const CompilationUnit* c_unit, int ssa_reg) {
   // In order to determine if the ssa reg is live out, we scan all the MIRs. We remember
   // the last SSA number of the same dalvik register. At the end, if it is different than ssa_reg,
@@ -2333,17 +2375,34 @@
 void BasicBlock::ErasePredecessor(BasicBlockId old_pred) {
   auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred);
   DCHECK(pos != predecessors.end());
-  predecessors.erase(pos);
+  // It's faster to move the back() to *pos than erase(pos).
+  *pos = predecessors.back();
+  predecessors.pop_back();
+  size_t idx = std::distance(predecessors.begin(), pos);
+  for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) {
+    if (static_cast<int>(mir->dalvikInsn.opcode) != kMirOpPhi) {
+      break;
+    }
+    DCHECK_EQ(mir->ssa_rep->num_uses - 1u, predecessors.size());
+    DCHECK_EQ(mir->meta.phi_incoming[idx], old_pred);
+    mir->meta.phi_incoming[idx] = mir->meta.phi_incoming[predecessors.size()];
+    mir->ssa_rep->uses[idx] = mir->ssa_rep->uses[predecessors.size()];
+    mir->ssa_rep->num_uses = predecessors.size();
+  }
 }
 
 void BasicBlock::UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred) {
   DCHECK_NE(new_pred, NullBasicBlockId);
   auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred);
-  if (pos != predecessors.end()) {
-    *pos = new_pred;
-  } else {
-    // If not found, add it.
-    predecessors.push_back(new_pred);
+  DCHECK(pos != predecessors.end());
+  *pos = new_pred;
+  size_t idx = std::distance(predecessors.begin(), pos);
+  for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) {
+    if (static_cast<int>(mir->dalvikInsn.opcode) != kMirOpPhi) {
+      break;
+    }
+    DCHECK_EQ(mir->meta.phi_incoming[idx], old_pred);
+    mir->meta.phi_incoming[idx] = new_pred;
   }
 }
 
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index cc215bd..80303f6 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -49,17 +49,14 @@
   kFormat35c,
   kFormat3rc,
   kFormatExtended,       // Extended format for extended MIRs.
-  kNullCheckSrc0,        // Null check of uses[0].
-  kNullCheckSrc1,        // Null check of uses[1].
-  kNullCheckSrc2,        // Null check of uses[2].
+  kNullCheckA,           // Null check of A.
+  kNullCheckB,           // Null check of B.
   kNullCheckOut0,        // Null check out outgoing arg0.
   kDstNonNull,           // May assume dst is non-null.
   kRetNonNull,           // May assume retval is non-null.
   kNullTransferSrc0,     // Object copy src[0] -> dst.
   kNullTransferSrcN,     // Phi null check state transfer.
-  kRangeCheckSrc1,       // Range check of uses[1].
-  kRangeCheckSrc2,       // Range check of uses[2].
-  kRangeCheckSrc3,       // Range check of uses[3].
+  kRangeCheckC,          // Range check of C.
   kFPA,
   kFPB,
   kFPC,
@@ -88,17 +85,14 @@
 #define DF_FORMAT_35C           (UINT64_C(1) << kFormat35c)
 #define DF_FORMAT_3RC           (UINT64_C(1) << kFormat3rc)
 #define DF_FORMAT_EXTENDED      (UINT64_C(1) << kFormatExtended)
-#define DF_NULL_CHK_0           (UINT64_C(1) << kNullCheckSrc0)
-#define DF_NULL_CHK_1           (UINT64_C(1) << kNullCheckSrc1)
-#define DF_NULL_CHK_2           (UINT64_C(1) << kNullCheckSrc2)
+#define DF_NULL_CHK_A           (UINT64_C(1) << kNullCheckA)
+#define DF_NULL_CHK_B           (UINT64_C(1) << kNullCheckB)
 #define DF_NULL_CHK_OUT0        (UINT64_C(1) << kNullCheckOut0)
 #define DF_NON_NULL_DST         (UINT64_C(1) << kDstNonNull)
 #define DF_NON_NULL_RET         (UINT64_C(1) << kRetNonNull)
 #define DF_NULL_TRANSFER_0      (UINT64_C(1) << kNullTransferSrc0)
 #define DF_NULL_TRANSFER_N      (UINT64_C(1) << kNullTransferSrcN)
-#define DF_RANGE_CHK_1          (UINT64_C(1) << kRangeCheckSrc1)
-#define DF_RANGE_CHK_2          (UINT64_C(1) << kRangeCheckSrc2)
-#define DF_RANGE_CHK_3          (UINT64_C(1) << kRangeCheckSrc3)
+#define DF_RANGE_CHK_C          (UINT64_C(1) << kRangeCheckC)
 #define DF_FP_A                 (UINT64_C(1) << kFPA)
 #define DF_FP_B                 (UINT64_C(1) << kFPB)
 #define DF_FP_C                 (UINT64_C(1) << kFPC)
@@ -117,14 +111,11 @@
 
 #define DF_HAS_DEFS             (DF_DA)
 
-#define DF_HAS_NULL_CHKS        (DF_NULL_CHK_0 | \
-                                 DF_NULL_CHK_1 | \
-                                 DF_NULL_CHK_2 | \
+#define DF_HAS_NULL_CHKS        (DF_NULL_CHK_A | \
+                                 DF_NULL_CHK_B | \
                                  DF_NULL_CHK_OUT0)
 
-#define DF_HAS_RANGE_CHKS       (DF_RANGE_CHK_1 | \
-                                 DF_RANGE_CHK_2 | \
-                                 DF_RANGE_CHK_3)
+#define DF_HAS_RANGE_CHKS       (DF_RANGE_CHK_C)
 
 #define DF_HAS_NR_CHKS          (DF_HAS_NULL_CHKS | \
                                  DF_HAS_RANGE_CHKS)
@@ -132,9 +123,10 @@
 #define DF_A_IS_REG             (DF_UA | DF_DA)
 #define DF_B_IS_REG             (DF_UB)
 #define DF_C_IS_REG             (DF_UC)
-#define DF_IS_GETTER_OR_SETTER  (DF_IS_GETTER | DF_IS_SETTER)
 #define DF_USES_FP              (DF_FP_A | DF_FP_B | DF_FP_C)
 #define DF_NULL_TRANSFER        (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)
+#define DF_IS_INVOKE            (DF_FORMAT_35C | DF_FORMAT_3RC)
+
 enum OatMethodAttributes {
   kIsLeaf,            // Method is leaf.
   kHasLoop,           // Method contains simple loop.
@@ -160,6 +152,7 @@
 #define MIR_CALLEE                      (1 << kMIRCallee)
 #define MIR_IGNORE_SUSPEND_CHECK        (1 << kMIRIgnoreSuspendCheck)
 #define MIR_DUP                         (1 << kMIRDup)
+#define MIR_MARK                        (1 << kMIRMark)
 #define MIR_STORE_NON_TEMPORAL          (1 << kMIRStoreNonTemporal)
 
 #define BLOCK_NAME_LEN 80
@@ -422,7 +415,12 @@
    *          remove itself from any predecessor edges, remove itself from any
    *          child's predecessor array.
    */
-  void Hide(CompilationUnit* c_unit);
+  void Hide(MIRGraph* mir_graph);
+
+  /**
+   *  @brief Kill the unreachable block and all blocks that become unreachable by killing this one.
+   */
+  void KillUnreachable(MIRGraph* mir_graph);
 
   /**
    * @brief Is ssa_reg the last SSA definition of that VR in the block?
@@ -1015,6 +1013,10 @@
     return GetFirstSpecialTempVR() + max_available_special_compiler_temps_;
   }
 
+  bool HasTryCatchBlocks() const {
+    return current_code_item_->tries_size_ != 0;
+  }
+
   void DumpCheckStats();
   MIR* FindMoveResult(BasicBlock* bb, MIR* mir);
   int SRegToVReg(int ssa_reg) const;
@@ -1150,6 +1152,10 @@
   void InsertPhiNodes();
   void DoDFSPreOrderSSARename(BasicBlock* block);
 
+  bool DfsOrdersUpToDate() const {
+    return dfs_orders_up_to_date_;
+  }
+
   /*
    * IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on
    * we can verify that all catch entries have native PC entries.
@@ -1246,6 +1252,7 @@
   ArenaVector<uint32_t> raw_use_counts_;  // Not weighted
   unsigned int num_reachable_blocks_;
   unsigned int max_num_reachable_blocks_;
+  bool dfs_orders_up_to_date_;
   ArenaVector<BasicBlockId> dfs_order_;
   ArenaVector<BasicBlockId> dfs_post_order_;
   ArenaVector<BasicBlockId> dom_post_order_traversal_;
@@ -1306,7 +1313,9 @@
   static const uint64_t oat_data_flow_attributes_[kMirOpLast];
   ArenaVector<BasicBlock*> gen_suspend_test_list_;  // List of blocks containing suspend tests
 
+  friend class MirOptimizationTest;
   friend class ClassInitCheckEliminationTest;
+  friend class NullCheckEliminationTest;
   friend class GlobalValueNumberingTest;
   friend class LocalValueNumberingTest;
   friend class TopologicalSortOrderTest;
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 84c056d..00528e5 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -752,51 +752,101 @@
 /* Combine any basic blocks terminated by instructions that we now know can't throw */
 void MIRGraph::CombineBlocks(struct BasicBlock* bb) {
   // Loop here to allow combining a sequence of blocks
-  while (true) {
-    // Check termination conditions
-    if ((bb->first_mir_insn == NULL)
-        || (bb->data_flow_info == NULL)
-        || (bb->block_type == kExceptionHandling)
-        || (bb->block_type == kExitBlock)
-        || (bb->block_type == kDead)
-        || (bb->taken == NullBasicBlockId)
-        || (GetBasicBlock(bb->taken)->block_type != kExceptionHandling)
-        || (bb->successor_block_list_type != kNotUsed)
-        || (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) != kMirOpCheck)) {
+  while ((bb->block_type == kDalvikByteCode) &&
+      (bb->last_mir_insn != nullptr) &&
+      (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) == kMirOpCheck)) {
+    MIR* mir = bb->last_mir_insn;
+    DCHECK(bb->first_mir_insn !=  nullptr);
+
+    // Grab the attributes from the paired opcode.
+    MIR* throw_insn = mir->meta.throw_insn;
+    uint64_t df_attributes = GetDataFlowAttributes(throw_insn);
+
+    // Don't combine if the throw_insn can still throw NPE.
+    if ((df_attributes & DF_HAS_NULL_CHKS) != 0 &&
+        (throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) {
+      break;
+    }
+    // Now whitelist specific instructions.
+    bool ok = false;
+    if ((df_attributes & DF_IFIELD) != 0) {
+      // Combine only if fast, otherwise weird things can happen.
+      const MirIFieldLoweringInfo& field_info = GetIFieldLoweringInfo(throw_insn);
+      ok = (df_attributes & DF_DA)  ? field_info.FastPut() : field_info.FastGet();
+    } else if ((df_attributes & DF_SFIELD) != 0) {
+      // Combine only if fast, otherwise weird things can happen.
+      const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(throw_insn);
+      bool fast = ((df_attributes & DF_DA)  ? field_info.FastPut() : field_info.FastGet());
+      // Don't combine if the SGET/SPUT can call <clinit>().
+      bool clinit = !field_info.IsInitialized() &&
+          (throw_insn->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0;
+      ok = fast && !clinit;
+    } else if ((df_attributes & DF_HAS_RANGE_CHKS) != 0) {
+      // Only AGET/APUT have range checks. We have processed the AGET/APUT null check above.
+      DCHECK_NE(throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK, 0);
+      ok = ((throw_insn->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0);
+    } else if ((throw_insn->dalvikInsn.FlagsOf() & Instruction::kThrow) == 0) {
+      // We can encounter a non-throwing insn here thanks to inlining or other optimizations.
+      ok = true;
+    } else if (throw_insn->dalvikInsn.opcode == Instruction::ARRAY_LENGTH ||
+        throw_insn->dalvikInsn.opcode == Instruction::FILL_ARRAY_DATA ||
+        static_cast<int>(throw_insn->dalvikInsn.opcode) == kMirOpNullCheck) {
+      // No more checks for these (null check was processed above).
+      ok = true;
+    }
+    if (!ok) {
       break;
     }
 
-    // Test the kMirOpCheck instruction
-    MIR* mir = bb->last_mir_insn;
-    // Grab the attributes from the paired opcode
-    MIR* throw_insn = mir->meta.throw_insn;
-    uint64_t df_attributes = GetDataFlowAttributes(throw_insn);
-    bool can_combine = true;
-    if (df_attributes & DF_HAS_NULL_CHKS) {
-      can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0);
-    }
-    if (df_attributes & DF_HAS_RANGE_CHKS) {
-      can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0);
-    }
-    if (!can_combine) {
-      break;
-    }
     // OK - got one.  Combine
     BasicBlock* bb_next = GetBasicBlock(bb->fall_through);
     DCHECK(!bb_next->catch_entry);
-    DCHECK_EQ(Predecessors(bb_next), 1U);
-    // Overwrite the kOpCheck insn with the paired opcode
+    DCHECK_EQ(bb_next->predecessors.size(), 1u);
+    // Overwrite the kMirOpCheck insn with the paired opcode.
     DCHECK_EQ(bb_next->first_mir_insn, throw_insn);
     *bb->last_mir_insn = *throw_insn;
+    // And grab the rest of the instructions from bb_next.
+    bb->last_mir_insn = bb_next->last_mir_insn;
+    throw_insn->next = nullptr;
+    bb_next->last_mir_insn = throw_insn;
+    // Mark acquired instructions as belonging to bb.
+    for (MIR* insn = mir; insn != nullptr; insn = insn->next) {
+      insn->bb = bb->id;
+    }
+    // Before we overwrite successors, remove their predecessor links to bb.
+    bb_next->ErasePredecessor(bb->id);
+    if (bb->taken != NullBasicBlockId) {
+      DCHECK_EQ(bb->successor_block_list_type, kNotUsed);
+      BasicBlock* bb_taken = GetBasicBlock(bb->taken);
+      // bb->taken will be overwritten below.
+      DCHECK_EQ(bb_taken->block_type, kExceptionHandling);
+      DCHECK_EQ(bb_taken->predecessors.size(), 1u);
+      DCHECK_EQ(bb_taken->predecessors[0], bb->id);
+      bb_taken->predecessors.clear();
+      bb_taken->block_type = kDead;
+      DCHECK(bb_taken->data_flow_info == nullptr);
+    } else {
+      DCHECK_EQ(bb->successor_block_list_type, kCatch);
+      for (SuccessorBlockInfo* succ_info : bb->successor_blocks) {
+        if (succ_info->block != NullBasicBlockId) {
+          BasicBlock* succ_bb = GetBasicBlock(succ_info->block);
+          DCHECK(succ_bb->catch_entry);
+          succ_bb->ErasePredecessor(bb->id);
+          if (succ_bb->predecessors.empty()) {
+            succ_bb->KillUnreachable(this);
+          }
+        }
+      }
+    }
     // Use the successor info from the next block
     bb->successor_block_list_type = bb_next->successor_block_list_type;
     bb->successor_blocks.swap(bb_next->successor_blocks);  // Swap instead of copying.
+    bb_next->successor_block_list_type = kNotUsed;
     // Use the ending block linkage from the next block
     bb->fall_through = bb_next->fall_through;
-    GetBasicBlock(bb->taken)->block_type = kDead;  // Kill the unused exception block
+    bb_next->fall_through = NullBasicBlockId;
     bb->taken = bb_next->taken;
-    // Include the rest of the instructions
-    bb->last_mir_insn = bb_next->last_mir_insn;
+    bb_next->taken = NullBasicBlockId;
     /*
      * If lower-half of pair of blocks to combine contained
      * a return or a conditional branch or an explicit throw,
@@ -805,15 +855,30 @@
     bb->terminated_by_return = bb_next->terminated_by_return;
     bb->conditional_branch = bb_next->conditional_branch;
     bb->explicit_throw = bb_next->explicit_throw;
+    // Merge the use_lvn flag.
+    bb->use_lvn |= bb_next->use_lvn;
+
+    // Kill the unused block.
+    bb_next->data_flow_info = nullptr;
 
     /*
      * NOTE: we aren't updating all dataflow info here.  Should either make sure this pass
      * happens after uses of i_dominated, dom_frontier or update the dataflow info here.
+     * NOTE: GVN uses bb->data_flow_info->live_in_v which is unaffected by the block merge.
      */
 
-    // Kill bb_next and remap now-dead id to parent
+    // Kill bb_next and remap now-dead id to parent.
     bb_next->block_type = kDead;
+    bb_next->data_flow_info = nullptr;  // Must be null for dead blocks. (Relied on by the GVN.)
     block_id_map_.Overwrite(bb_next->id, bb->id);
+    // Update predecessors in children.
+    ChildBlockIterator iter(bb, this);
+    for (BasicBlock* child = iter.Next(); child != nullptr; child = iter.Next()) {
+      child->UpdatePredecessor(bb_next->id, bb->id);
+    }
+
+    // DFS orders are not up to date anymore.
+    dfs_orders_up_to_date_ = false;
 
     // Now, loop back and see if we can keep going
   }
@@ -827,12 +892,21 @@
 
   DCHECK(temp_scoped_alloc_.get() == nullptr);
   temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
-  temp_bit_vector_size_ = GetNumSSARegs();
+  temp_bit_vector_size_ = GetNumOfCodeVRs();
   temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
       temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck);
   temp_bit_matrix_ = static_cast<ArenaBitVector**>(
       temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * GetNumBlocks(), kArenaAllocMisc));
   std::fill_n(temp_bit_matrix_, GetNumBlocks(), nullptr);
+
+  // reset MIR_MARK
+  AllNodesIterator iter(this);
+  for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
+    for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+      mir->optimization_flags &= ~MIR_MARK;
+    }
+  }
+
   return true;
 }
 
@@ -840,100 +914,96 @@
  * Eliminate unnecessary null checks for a basic block.
  */
 bool MIRGraph::EliminateNullChecks(BasicBlock* bb) {
-  if (bb->data_flow_info == nullptr) return false;
+  if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) {
+    // Ignore the kExitBlock as well.
+    DCHECK(bb->first_mir_insn == nullptr);
+    return false;
+  }
 
-  ArenaBitVector* ssa_regs_to_check = temp_bit_vector_;
+  ArenaBitVector* vregs_to_check = temp_bit_vector_;
   /*
    * Set initial state. Catch blocks don't need any special treatment.
    */
   if (bb->block_type == kEntryBlock) {
-    ssa_regs_to_check->ClearAllBits();
+    vregs_to_check->ClearAllBits();
     // Assume all ins are objects.
     for (uint16_t in_reg = GetFirstInVR();
          in_reg < GetNumOfCodeVRs(); in_reg++) {
-      ssa_regs_to_check->SetBit(in_reg);
+      vregs_to_check->SetBit(in_reg);
     }
     if ((cu_->access_flags & kAccStatic) == 0) {
-      // If non-static method, mark "this" as non-null
+      // If non-static method, mark "this" as non-null.
       int this_reg = GetFirstInVR();
-      ssa_regs_to_check->ClearBit(this_reg);
+      vregs_to_check->ClearBit(this_reg);
     }
-  } else if (bb->predecessors.size() == 1) {
-    BasicBlock* pred_bb = GetBasicBlock(bb->predecessors[0]);
-    // pred_bb must have already been processed at least once.
-    DCHECK(temp_bit_matrix_[pred_bb->id] != nullptr);
-    ssa_regs_to_check->Copy(temp_bit_matrix_[pred_bb->id]);
-    if (pred_bb->block_type == kDalvikByteCode) {
-      // Check to see if predecessor had an explicit null-check.
-      MIR* last_insn = pred_bb->last_mir_insn;
-      if (last_insn != nullptr) {
-        Instruction::Code last_opcode = last_insn->dalvikInsn.opcode;
-        if (last_opcode == Instruction::IF_EQZ) {
-          if (pred_bb->fall_through == bb->id) {
-            // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that
-            // it can't be null.
-            ssa_regs_to_check->ClearBit(last_insn->ssa_rep->uses[0]);
-          }
-        } else if (last_opcode == Instruction::IF_NEZ) {
-          if (pred_bb->taken == bb->id) {
-            // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be
-            // null.
-            ssa_regs_to_check->ClearBit(last_insn->ssa_rep->uses[0]);
+  } else {
+    DCHECK_EQ(bb->block_type, kDalvikByteCode);
+    // Starting state is union of all incoming arcs.
+    bool copied_first = false;
+    for (BasicBlockId pred_id : bb->predecessors) {
+      if (temp_bit_matrix_[pred_id] == nullptr) {
+        continue;
+      }
+      BasicBlock* pred_bb = GetBasicBlock(pred_id);
+      DCHECK(pred_bb != nullptr);
+      MIR* null_check_insn = nullptr;
+      if (pred_bb->block_type == kDalvikByteCode) {
+        // Check to see if predecessor had an explicit null-check.
+        MIR* last_insn = pred_bb->last_mir_insn;
+        if (last_insn != nullptr) {
+          Instruction::Code last_opcode = last_insn->dalvikInsn.opcode;
+          if ((last_opcode == Instruction::IF_EQZ && pred_bb->fall_through == bb->id) ||
+              (last_opcode == Instruction::IF_NEZ && pred_bb->taken == bb->id)) {
+            // Remember the null check insn if there's no other predecessor requiring null check.
+            if (!copied_first || !vregs_to_check->IsBitSet(last_insn->dalvikInsn.vA)) {
+              null_check_insn = last_insn;
+            }
           }
         }
       }
-    }
-  } else {
-    // Starting state is union of all incoming arcs
-    bool copied_first = false;
-    for (BasicBlockId pred_id : bb->predecessors) {
-      BasicBlock* pred_bb = GetBasicBlock(pred_id);
-      DCHECK(pred_bb != nullptr);
-      DCHECK(pred_bb->data_flow_info != nullptr);
-      if (temp_bit_matrix_[pred_bb->id] == nullptr) {
-        continue;
-      }
       if (!copied_first) {
         copied_first = true;
-        ssa_regs_to_check->Copy(temp_bit_matrix_[pred_bb->id]);
+        vregs_to_check->Copy(temp_bit_matrix_[pred_id]);
       } else {
-        ssa_regs_to_check->Union(temp_bit_matrix_[pred_bb->id]);
+        vregs_to_check->Union(temp_bit_matrix_[pred_id]);
+      }
+      if (null_check_insn != nullptr) {
+        vregs_to_check->ClearBit(null_check_insn->dalvikInsn.vA);
       }
     }
     DCHECK(copied_first);  // At least one predecessor must have been processed before this bb.
   }
-  // At this point, ssa_regs_to_check shows which sregs have an object definition with
+  // At this point, vregs_to_check shows which sregs have an object definition with
   // no intervening uses.
 
   // Walk through the instruction in the block, updating as necessary
   for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
-    if (mir->ssa_rep == NULL) {
-        continue;
-    }
-
     uint64_t df_attributes = GetDataFlowAttributes(mir);
 
+    DCHECK_EQ(df_attributes & DF_NULL_TRANSFER_N, 0u);  // No Phis yet.
+
     // Might need a null check?
     if (df_attributes & DF_HAS_NULL_CHKS) {
-      int src_idx;
-      if (df_attributes & DF_NULL_CHK_1) {
-        src_idx = 1;
-      } else if (df_attributes & DF_NULL_CHK_2) {
-        src_idx = 2;
+      int src_vreg;
+      if (df_attributes & DF_NULL_CHK_OUT0) {
+        DCHECK_NE(df_attributes & DF_IS_INVOKE, 0u);
+        src_vreg = mir->dalvikInsn.vC;
+      } else if (df_attributes & DF_NULL_CHK_B) {
+        DCHECK_NE(df_attributes & DF_REF_B, 0u);
+        src_vreg = mir->dalvikInsn.vB;
       } else {
-        src_idx = 0;
+        DCHECK_NE(df_attributes & DF_NULL_CHK_A, 0u);
+        DCHECK_NE(df_attributes & DF_REF_A, 0u);
+        src_vreg = mir->dalvikInsn.vA;
       }
-      int src_sreg = mir->ssa_rep->uses[src_idx];
-      if (!ssa_regs_to_check->IsBitSet(src_sreg)) {
+      if (!vregs_to_check->IsBitSet(src_vreg)) {
         // Eliminate the null check.
-        mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+        mir->optimization_flags |= MIR_MARK;
       } else {
         // Do the null check.
-        // TODO: Rewrite the pass to converge first before doing any modifications so that
-        // we don't lose the MIR_IGNORE_NULL_CHECK here if previously set by some other pass.
-        mir->optimization_flags &= ~MIR_IGNORE_NULL_CHECK;
-        // Mark s_reg as null-checked
-        ssa_regs_to_check->ClearBit(src_sreg);
+        mir->optimization_flags &= ~MIR_MARK;
+        // Mark src_vreg as null-checked.
+        vregs_to_check->ClearBit(src_vreg);
       }
     }
 
@@ -947,66 +1017,41 @@
      * Note: we can't tell if a CONST definition might be used as an object, so treat
      * them all as object definitions.
      */
-    if (((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A)) ||
+    if ((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A) ||
         (df_attributes & DF_SETS_CONST))  {
-      ssa_regs_to_check->SetBit(mir->ssa_rep->defs[0]);
+      vregs_to_check->SetBit(mir->dalvikInsn.vA);
     }
 
-    // Now, remove mark from all object definitions we know are non-null.
+    // Then, remove mark from all object definitions we know are non-null.
     if (df_attributes & DF_NON_NULL_DST) {
       // Mark target of NEW* as non-null
-      ssa_regs_to_check->ClearBit(mir->ssa_rep->defs[0]);
+      DCHECK_NE(df_attributes & DF_REF_A, 0u);
+      vregs_to_check->ClearBit(mir->dalvikInsn.vA);
     }
 
     // Mark non-null returns from invoke-style NEW*
     if (df_attributes & DF_NON_NULL_RET) {
       MIR* next_mir = mir->next;
       // Next should be an MOVE_RESULT_OBJECT
-      if (next_mir &&
-          next_mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
-        // Mark as null checked
-        ssa_regs_to_check->ClearBit(next_mir->ssa_rep->defs[0]);
+      if (UNLIKELY(next_mir == nullptr)) {
+        // The MethodVerifier makes sure there's no MOVE_RESULT at the catch entry or branch
+        // target, so the MOVE_RESULT cannot be broken away into another block.
+        LOG(WARNING) << "Unexpected end of block following new";
+      } else if (UNLIKELY(next_mir->dalvikInsn.opcode != Instruction::MOVE_RESULT_OBJECT)) {
+        LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode;
       } else {
-        if (next_mir) {
-          LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode;
-        } else if (bb->fall_through != NullBasicBlockId) {
-          // Look in next basic block
-          struct BasicBlock* next_bb = GetBasicBlock(bb->fall_through);
-          for (MIR* tmir = next_bb->first_mir_insn; tmir != NULL;
-            tmir =tmir->next) {
-            if (MIR::DecodedInstruction::IsPseudoMirOp(tmir->dalvikInsn.opcode)) {
-              continue;
-            }
-            // First non-pseudo should be MOVE_RESULT_OBJECT
-            if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
-              // Mark as null checked
-              ssa_regs_to_check->ClearBit(tmir->ssa_rep->defs[0]);
-            } else {
-              LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode;
-            }
-            break;
-          }
-        }
+        // Mark as null checked.
+        vregs_to_check->ClearBit(next_mir->dalvikInsn.vA);
       }
     }
 
-    /*
-     * Propagate nullcheck state on register copies (including
-     * Phi pseudo copies.  For the latter, nullcheck state is
-     * the "or" of all the Phi's operands.
-     */
-    if (df_attributes & (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) {
-      int tgt_sreg = mir->ssa_rep->defs[0];
-      int operands = (df_attributes & DF_NULL_TRANSFER_0) ? 1 :
-          mir->ssa_rep->num_uses;
-      bool needs_null_check = false;
-      for (int i = 0; i < operands; i++) {
-        needs_null_check |= ssa_regs_to_check->IsBitSet(mir->ssa_rep->uses[i]);
-      }
-      if (needs_null_check) {
-        ssa_regs_to_check->SetBit(tgt_sreg);
+    // Propagate null check state on register copies.
+    if (df_attributes & DF_NULL_TRANSFER_0) {
+      DCHECK_EQ(df_attributes | ~(DF_DA | DF_REF_A | DF_UB | DF_REF_B), static_cast<uint64_t>(-1));
+      if (vregs_to_check->IsBitSet(mir->dalvikInsn.vB)) {
+        vregs_to_check->SetBit(mir->dalvikInsn.vA);
       } else {
-        ssa_regs_to_check->ClearBit(tgt_sreg);
+        vregs_to_check->ClearBit(mir->dalvikInsn.vA);
       }
     }
   }
@@ -1016,15 +1061,15 @@
   ArenaBitVector* old_ending_ssa_regs_to_check = temp_bit_matrix_[bb->id];
   if (old_ending_ssa_regs_to_check == nullptr) {
     DCHECK(temp_scoped_alloc_.get() != nullptr);
-    nce_changed = ssa_regs_to_check->GetHighestBitSet() != -1;
-    temp_bit_matrix_[bb->id] = ssa_regs_to_check;
-    // Create a new ssa_regs_to_check for next BB.
+    nce_changed = vregs_to_check->GetHighestBitSet() != -1;
+    temp_bit_matrix_[bb->id] = vregs_to_check;
+    // Create a new vregs_to_check for next BB.
     temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
         temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck);
-  } else if (!ssa_regs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) {
+  } else if (!vregs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) {
     nce_changed = true;
-    temp_bit_matrix_[bb->id] = ssa_regs_to_check;
-    temp_bit_vector_ = old_ending_ssa_regs_to_check;  // Reuse for ssa_regs_to_check for next BB.
+    temp_bit_matrix_[bb->id] = vregs_to_check;
+    temp_bit_vector_ = old_ending_ssa_regs_to_check;  // Reuse for vregs_to_check for next BB.
   }
   return nce_changed;
 }
@@ -1036,6 +1081,18 @@
   temp_bit_matrix_ = nullptr;
   DCHECK(temp_scoped_alloc_.get() != nullptr);
   temp_scoped_alloc_.reset();
+
+  // converge MIR_MARK with MIR_IGNORE_NULL_CHECK
+  AllNodesIterator iter(this);
+  for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
+    for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+      constexpr int kMarkToIgnoreNullCheckShift = kMIRMark - kMIRIgnoreNullCheck;
+      COMPILE_ASSERT(kMarkToIgnoreNullCheckShift > 0, check_valid_shift_right);
+      uint16_t mirMarkAdjustedToIgnoreNullCheck =
+          (mir->optimization_flags & MIR_MARK) >> kMarkToIgnoreNullCheckShift;
+      mir->optimization_flags |= mirMarkAdjustedToIgnoreNullCheck;
+    }
+  }
 }
 
 /*
@@ -1100,26 +1157,27 @@
     // First, find all SGET/SPUTs that may need class initialization checks, record INVOKE_STATICs.
     AllNodesIterator iter(this);
     for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
-      for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
-        DCHECK(bb->data_flow_info != nullptr);
-        if (mir->dalvikInsn.opcode >= Instruction::SGET &&
-            mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
-          const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir);
-          uint16_t index = 0xffffu;
-          if (!field_info.IsInitialized()) {
-            DCHECK_LT(class_to_index_map.size(), 0xffffu);
-            MapEntry entry = {
-                // Treat unresolved fields as if each had its own class.
-                field_info.IsResolved() ? field_info.DeclaringDexFile()
-                                        : nullptr,
-                field_info.IsResolved() ? field_info.DeclaringClassIndex()
-                                        : field_info.FieldIndex(),
-                static_cast<uint16_t>(class_to_index_map.size())
-            };
-            index = class_to_index_map.insert(entry).first->index;
+      if (bb->block_type == kDalvikByteCode) {
+        for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+          if (mir->dalvikInsn.opcode >= Instruction::SGET &&
+              mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
+            const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir);
+            uint16_t index = 0xffffu;
+            if (!field_info.IsInitialized()) {
+              DCHECK_LT(class_to_index_map.size(), 0xffffu);
+              MapEntry entry = {
+                  // Treat unresolved fields as if each had its own class.
+                  field_info.IsResolved() ? field_info.DeclaringDexFile()
+                                          : nullptr,
+                  field_info.IsResolved() ? field_info.DeclaringClassIndex()
+                                          : field_info.FieldIndex(),
+                  static_cast<uint16_t>(class_to_index_map.size())
+              };
+              index = class_to_index_map.insert(entry).first->index;
+            }
+            // Using offset/2 for index into temp_insn_data_.
+            temp_insn_data_[mir->offset / 2u] = index;
           }
-          // Using offset/2 for index into temp_insn_data_.
-          temp_insn_data_[mir->offset / 2u] = index;
         }
       }
     }
@@ -1148,7 +1206,9 @@
  */
 bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) {
   DCHECK_EQ((cu_->disable_opt & (1 << kClassInitCheckElimination)), 0u);
-  if (bb->data_flow_info == nullptr) {
+  if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) {
+    // Ignore the kExitBlock as well.
+    DCHECK(bb->first_mir_insn == nullptr);
     return false;
   }
 
@@ -1163,7 +1223,6 @@
     BasicBlock* pred_bb = GetBasicBlock(bb->predecessors[0]);
     // pred_bb must have already been processed at least once.
     DCHECK(pred_bb != nullptr);
-    DCHECK(pred_bb->data_flow_info != nullptr);
     DCHECK(temp_bit_matrix_[pred_bb->id] != nullptr);
     classes_to_check->Copy(temp_bit_matrix_[pred_bb->id]);
   } else {
@@ -1172,7 +1231,6 @@
     for (BasicBlockId pred_id : bb->predecessors) {
       BasicBlock* pred_bb = GetBasicBlock(pred_id);
       DCHECK(pred_bb != nullptr);
-      DCHECK(pred_bb->data_flow_info != nullptr);
       if (temp_bit_matrix_[pred_bb->id] == nullptr) {
         continue;
       }
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index 55e547e..337d4ef 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -23,15 +23,8 @@
 
 namespace art {
 
-class ClassInitCheckEliminationTest : public testing::Test {
+class MirOptimizationTest : public testing::Test {
  protected:
-  struct SFieldDef {
-    uint16_t field_idx;
-    uintptr_t declaring_dex_file;
-    uint16_t declaring_class_idx;
-    uint16_t declaring_field_idx;
-  };
-
   struct BBDef {
     static constexpr size_t kMaxSuccessors = 4;
     static constexpr size_t kMaxPredecessors = 4;
@@ -44,9 +37,12 @@
   };
 
   struct MIRDef {
-    Instruction::Code opcode;
     BasicBlockId bbid;
-    uint32_t field_or_method_info;
+    Instruction::Code opcode;
+    uint32_t field_info;
+    uint32_t vA;
+    uint32_t vB;
+    uint32_t vC;
   };
 
 #define DEF_SUCC0() \
@@ -72,32 +68,6 @@
 #define DEF_BB(type, succ, pred) \
     { type, succ, pred }
 
-#define DEF_MIR(opcode, bb, field_info) \
-    { opcode, bb, field_info }
-
-  void DoPrepareSFields(const SFieldDef* defs, size_t count) {
-    cu_.mir_graph->sfield_lowering_infos_.clear();
-    cu_.mir_graph->sfield_lowering_infos_.reserve(count);
-    for (size_t i = 0u; i != count; ++i) {
-      const SFieldDef* def = &defs[i];
-      MirSFieldLoweringInfo field_info(def->field_idx);
-      if (def->declaring_dex_file != 0u) {
-        field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
-        field_info.declaring_class_idx_ = def->declaring_class_idx;
-        field_info.declaring_field_idx_ = def->declaring_field_idx;
-        field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic;
-      }
-      ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
-      ASSERT_FALSE(field_info.IsInitialized());
-      cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
-    }
-  }
-
-  template <size_t count>
-  void PrepareSFields(const SFieldDef (&defs)[count]) {
-    DoPrepareSFields(defs, count);
-  }
-
   void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
     cu_.mir_graph->block_id_map_.clear();
     cu_.mir_graph->block_list_.clear();
@@ -145,6 +115,63 @@
     DoPrepareBasicBlocks(defs, count);
   }
 
+  void PrepareSingleBlock() {
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)),
+    };
+    PrepareBasicBlocks(bbs);
+  }
+
+  void PrepareDiamond() {
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),
+    };
+    PrepareBasicBlocks(bbs);
+  }
+
+  void PrepareLoop() {
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)),  // "taken" loops to self.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+    };
+    PrepareBasicBlocks(bbs);
+  }
+
+  void PrepareCatch() {
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),     // The top.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),     // The throwing insn.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),     // Catch handler.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),  // The merged block.
+    };
+    PrepareBasicBlocks(bbs);
+    BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u);
+    catch_handler->catch_entry = true;
+    // Add successor block info to the check block.
+    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));
+    successor_block_info->block = catch_handler->id;
+    check_bb->successor_blocks.push_back(successor_block_info);
+  }
+
   void DoPrepareMIRs(const MIRDef* defs, size_t count) {
     mir_count_ = count;
     mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, kArenaAllocMIR));
@@ -157,9 +184,15 @@
       BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid];
       bb->AppendMIR(mir);
       if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) {
-        ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size());
-        mir->meta.sfield_lowering_info = def->field_or_method_info;
+        ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size());
+        mir->meta.sfield_lowering_info = def->field_info;
+      } else if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) {
+        ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size());
+        mir->meta.ifield_lowering_info = def->field_info;
       }
+      mir->dalvikInsn.vA = def->vA;
+      mir->dalvikInsn.vB = def->vB;
+      mir->dalvikInsn.vC = def->vC;
       mir->ssa_rep = nullptr;
       mir->offset = 2 * i;  // All insns need to be at least 2 code units long.
       mir->optimization_flags = 0u;
@@ -179,29 +212,14 @@
     DoPrepareMIRs(defs, count);
   }
 
-  void PerformClassInitCheckElimination() {
-    cu_.mir_graph->SSATransformationStart();
-    cu_.mir_graph->ComputeDFSOrders();
-    cu_.mir_graph->ComputeDominators();
-    cu_.mir_graph->ComputeTopologicalSortOrder();
-    cu_.mir_graph->SSATransformationEnd();
-    bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate();
-    ASSERT_TRUE(gate_result);
-    LoopRepeatingTopologicalSortIterator iterator(cu_.mir_graph.get());
-    bool change = false;
-    for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
-      change = cu_.mir_graph->EliminateClassInitChecks(bb);
-    }
-    cu_.mir_graph->EliminateClassInitChecksEnd();
-  }
-
-  ClassInitCheckEliminationTest()
+  MirOptimizationTest()
       : pool_(),
         cu_(&pool_),
         mir_count_(0u),
         mirs_(nullptr),
         code_item_(nullptr) {
     cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
+    cu_.access_flags = kAccStatic;  // Don't let "this" interfere with this test.
   }
 
   ArenaPool pool_;
@@ -211,6 +229,109 @@
   DexFile::CodeItem* code_item_;
 };
 
+class ClassInitCheckEliminationTest : public MirOptimizationTest {
+ protected:
+  struct SFieldDef {
+    uint16_t field_idx;
+    uintptr_t declaring_dex_file;
+    uint16_t declaring_class_idx;
+    uint16_t declaring_field_idx;
+  };
+
+  void DoPrepareSFields(const SFieldDef* defs, size_t count) {
+    cu_.mir_graph->sfield_lowering_infos_.clear();
+    cu_.mir_graph->sfield_lowering_infos_.reserve(count);
+    for (size_t i = 0u; i != count; ++i) {
+      const SFieldDef* def = &defs[i];
+      MirSFieldLoweringInfo field_info(def->field_idx);
+      if (def->declaring_dex_file != 0u) {
+        field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
+        field_info.declaring_class_idx_ = def->declaring_class_idx;
+        field_info.declaring_field_idx_ = def->declaring_field_idx;
+        field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic;
+      }
+      ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
+      ASSERT_FALSE(field_info.IsInitialized());
+      cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
+    }
+  }
+
+  template <size_t count>
+  void PrepareSFields(const SFieldDef (&defs)[count]) {
+    DoPrepareSFields(defs, count);
+  }
+
+  void PerformClassInitCheckElimination() {
+    cu_.mir_graph->ComputeDFSOrders();
+    bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate();
+    ASSERT_TRUE(gate_result);
+    RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get());
+    bool change = false;
+    for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
+      change = cu_.mir_graph->EliminateClassInitChecks(bb);
+    }
+    cu_.mir_graph->EliminateClassInitChecksEnd();
+  }
+
+  ClassInitCheckEliminationTest()
+      : MirOptimizationTest() {
+  }
+};
+
+class NullCheckEliminationTest : public MirOptimizationTest {
+ protected:
+  struct IFieldDef {
+    uint16_t field_idx;
+    uintptr_t declaring_dex_file;
+    uint16_t declaring_class_idx;
+    uint16_t declaring_field_idx;
+  };
+
+  void DoPrepareIFields(const IFieldDef* defs, size_t count) {
+    cu_.mir_graph->ifield_lowering_infos_.clear();
+    cu_.mir_graph->ifield_lowering_infos_.reserve(count);
+    for (size_t i = 0u; i != count; ++i) {
+      const IFieldDef* def = &defs[i];
+      MirIFieldLoweringInfo field_info(def->field_idx);
+      if (def->declaring_dex_file != 0u) {
+        field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
+        field_info.declaring_class_idx_ = def->declaring_class_idx;
+        field_info.declaring_field_idx_ = def->declaring_field_idx;
+      }
+      ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
+      cu_.mir_graph->ifield_lowering_infos_.push_back(field_info);
+    }
+  }
+
+  template <size_t count>
+  void PrepareIFields(const IFieldDef (&defs)[count]) {
+    DoPrepareIFields(defs, count);
+  }
+
+  void PerformNullCheckElimination() {
+    // Make vregs in range [100, 1000) input registers, i.e. requiring a null check.
+    code_item_->registers_size_ = 1000;
+    code_item_->ins_size_ = 900;
+
+    cu_.mir_graph->ComputeDFSOrders();
+    bool gate_result = cu_.mir_graph->EliminateNullChecksGate();
+    ASSERT_TRUE(gate_result);
+    RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get());
+    bool change = false;
+    for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
+      change = cu_.mir_graph->EliminateNullChecks(bb);
+    }
+    cu_.mir_graph->EliminateNullChecksEnd();
+  }
+
+  NullCheckEliminationTest()
+      : MirOptimizationTest() {
+  }
+};
+
+#define DEF_SGET_SPUT_V0(bb, opcode, field_info) \
+    { bb, opcode, field_info, 0u, 0u, 0u }
+
 TEST_F(ClassInitCheckEliminationTest, SingleBlock) {
   static const SFieldDef sfields[] = {
       { 0u, 1u, 0u, 0u },
@@ -220,31 +341,25 @@
       { 4u, 1u, 3u, 4u },  // Same declaring class as sfield[3].
       { 5u, 0u, 0u, 0u },  // Unresolved.
   };
-  static const BBDef bbs[] = {
-      DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
-      DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
-      DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)),
-  };
   static const MIRDef mirs[] = {
-      DEF_MIR(Instruction::SPUT, 3u, 5u),  // Unresolved.
-      DEF_MIR(Instruction::SPUT, 3u, 0u),
-      DEF_MIR(Instruction::SGET, 3u, 1u),
-      DEF_MIR(Instruction::SGET, 3u, 2u),
-      DEF_MIR(Instruction::SGET, 3u, 5u),  // Unresolved.
-      DEF_MIR(Instruction::SGET, 3u, 0u),
-      DEF_MIR(Instruction::SGET, 3u, 1u),
-      DEF_MIR(Instruction::SGET, 3u, 2u),
-      DEF_MIR(Instruction::SGET, 3u, 5u),  // Unresolved.
-      DEF_MIR(Instruction::SGET, 3u, 3u),
-      DEF_MIR(Instruction::SGET, 3u, 4u),
+      DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 5u),  // Unresolved.
+      DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 0u),
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u),
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u),
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 5u),  // Unresolved.
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u),
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u),
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u),
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 5u),  // Unresolved.
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 3u),
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 4u),
   };
   static const bool expected_ignore_clinit_check[] = {
       false, false, false, false, true, true, true, true, true, false, true
   };
 
   PrepareSFields(sfields);
-  PrepareBasicBlocks(bbs);
+  PrepareSingleBlock();
   PrepareMIRs(mirs);
   PerformClassInitCheckElimination();
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
@@ -268,40 +383,31 @@
       { 9u, 1u, 8u, 9u },  // Same declaring class as sfield[8].
       { 10u, 0u, 0u, 0u },  // Unresolved.
   };
-  static const BBDef bbs[] = {
-      DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
-      DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
-      DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),
-  };
   static const MIRDef mirs[] = {
       // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
-      DEF_MIR(Instruction::SGET, 3u, 10u),  // Unresolved.
-      DEF_MIR(Instruction::SPUT, 3u, 10u),  // Unresolved.
-      DEF_MIR(Instruction::SPUT, 3u, 0u),
-      DEF_MIR(Instruction::SGET, 6u, 0u),  // Eliminated (block #3 dominates #6).
-      DEF_MIR(Instruction::SPUT, 4u, 1u),
-      DEF_MIR(Instruction::SGET, 6u, 1u),  // Not eliminated (block #4 doesn't dominate #6).
-      DEF_MIR(Instruction::SGET, 3u, 2u),
-      DEF_MIR(Instruction::SGET, 4u, 2u),  // Eliminated (block #3 dominates #4).
-      DEF_MIR(Instruction::SGET, 3u, 3u),
-      DEF_MIR(Instruction::SGET, 5u, 3u),  // Eliminated (block #3 dominates #5).
-      DEF_MIR(Instruction::SGET, 3u, 4u),
-      DEF_MIR(Instruction::SGET, 6u, 4u),  // Eliminated (block #3 dominates #6).
-      DEF_MIR(Instruction::SGET, 4u, 5u),
-      DEF_MIR(Instruction::SGET, 6u, 5u),  // Not eliminated (block #4 doesn't dominate #6).
-      DEF_MIR(Instruction::SGET, 5u, 6u),
-      DEF_MIR(Instruction::SGET, 6u, 6u),  // Not eliminated (block #5 doesn't dominate #6).
-      DEF_MIR(Instruction::SGET, 4u, 7u),
-      DEF_MIR(Instruction::SGET, 5u, 7u),
-      DEF_MIR(Instruction::SGET, 6u, 7u),  // Eliminated (initialized in both blocks #3 and #4).
-      DEF_MIR(Instruction::SGET, 4u, 8u),
-      DEF_MIR(Instruction::SGET, 5u, 9u),
-      DEF_MIR(Instruction::SGET, 6u, 8u),  // Eliminated (with sfield[9] in block #5).
-      DEF_MIR(Instruction::SPUT, 6u, 9u),  // Eliminated (with sfield[8] in block #4).
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 10u),  // Unresolved.
+      DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 10u),  // Unresolved.
+      DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 0u),
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 0u),  // Eliminated (BB #3 dominates #6).
+      DEF_SGET_SPUT_V0(4u, Instruction::SPUT, 1u),
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 1u),  // Not eliminated (BB #4 doesn't dominate #6).
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u),
+      DEF_SGET_SPUT_V0(4u, Instruction::SGET, 2u),  // Eliminated (BB #3 dominates #4).
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 3u),
+      DEF_SGET_SPUT_V0(5u, Instruction::SGET, 3u),  // Eliminated (BB #3 dominates #5).
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 4u),
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 4u),  // Eliminated (BB #3 dominates #6).
+      DEF_SGET_SPUT_V0(4u, Instruction::SGET, 5u),
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 5u),  // Not eliminated (BB #4 doesn't dominate #6).
+      DEF_SGET_SPUT_V0(5u, Instruction::SGET, 6u),
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 6u),  // Not eliminated (BB #5 doesn't dominate #6).
+      DEF_SGET_SPUT_V0(4u, Instruction::SGET, 7u),
+      DEF_SGET_SPUT_V0(5u, Instruction::SGET, 7u),
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 7u),  // Eliminated (initialized in both #3 and #4).
+      DEF_SGET_SPUT_V0(4u, Instruction::SGET, 8u),
+      DEF_SGET_SPUT_V0(5u, Instruction::SGET, 9u),
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 8u),  // Eliminated (with sfield[9] in BB #5).
+      DEF_SGET_SPUT_V0(6u, Instruction::SPUT, 9u),  // Eliminated (with sfield[8] in BB #4).
   };
   static const bool expected_ignore_clinit_check[] = {
       false, true,          // Unresolved: sfield[10], method[2]
@@ -317,7 +423,7 @@
   };
 
   PrepareSFields(sfields);
-  PrepareBasicBlocks(bbs);
+  PrepareDiamond();
   PrepareMIRs(mirs);
   PerformClassInitCheckElimination();
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
@@ -332,26 +438,18 @@
       { 0u, 1u, 0u, 0u },
       { 1u, 1u, 1u, 1u },
   };
-  static const BBDef bbs[] = {
-      DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
-      DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
-      DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)),  // "taken" loops to self.
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
-  };
   static const MIRDef mirs[] = {
-      DEF_MIR(Instruction::SGET, 3u, 0u),
-      DEF_MIR(Instruction::SGET, 4u, 1u),
-      DEF_MIR(Instruction::SGET, 5u, 0u),  // Eliminated.
-      DEF_MIR(Instruction::SGET, 5u, 1u),  // Eliminated.
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u),
+      DEF_SGET_SPUT_V0(4u, Instruction::SGET, 1u),
+      DEF_SGET_SPUT_V0(5u, Instruction::SGET, 0u),  // Eliminated.
+      DEF_SGET_SPUT_V0(5u, Instruction::SGET, 1u),  // Eliminated.
   };
   static const bool expected_ignore_clinit_check[] = {
       false, false, true, true
   };
 
   PrepareSFields(sfields);
-  PrepareBasicBlocks(bbs);
+  PrepareLoop();
   PrepareMIRs(mirs);
   PerformClassInitCheckElimination();
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
@@ -368,42 +466,24 @@
       { 2u, 1u, 2u, 2u },
       { 3u, 1u, 3u, 3u },
   };
-  static const BBDef bbs[] = {
-      DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
-      DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
-      DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),     // The top.
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),     // The throwing insn.
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),     // Catch handler.
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),  // The merged block.
-  };
   static const MIRDef mirs[] = {
-      DEF_MIR(Instruction::SGET, 3u, 0u),  // Before the exception edge.
-      DEF_MIR(Instruction::SGET, 3u, 1u),  // Before the exception edge.
-      DEF_MIR(Instruction::SGET, 4u, 2u),  // After the exception edge.
-      DEF_MIR(Instruction::SGET, 4u, 3u),  // After the exception edge.
-      DEF_MIR(Instruction::SGET, 5u, 0u),  // In catch handler; class init check eliminated.
-      DEF_MIR(Instruction::SGET, 5u, 2u),  // In catch handler; class init check not eliminated.
-      DEF_MIR(Instruction::SGET, 6u, 0u),  // Class init check eliminated.
-      DEF_MIR(Instruction::SGET, 6u, 1u),  // Class init check eliminated.
-      DEF_MIR(Instruction::SGET, 6u, 2u),  // Class init check eliminated.
-      DEF_MIR(Instruction::SGET, 6u, 3u),  // Class init check not eliminated.
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u),  // Before the exception edge.
+      DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u),  // Before the exception edge.
+      DEF_SGET_SPUT_V0(4u, Instruction::SGET, 2u),  // After the exception edge.
+      DEF_SGET_SPUT_V0(4u, Instruction::SGET, 3u),  // After the exception edge.
+      DEF_SGET_SPUT_V0(5u, Instruction::SGET, 0u),  // In catch handler; clinit check eliminated.
+      DEF_SGET_SPUT_V0(5u, Instruction::SGET, 2u),  // In catch handler; clinit check not eliminated.
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 0u),  // Class init check eliminated.
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 1u),  // Class init check eliminated.
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 2u),  // Class init check eliminated.
+      DEF_SGET_SPUT_V0(6u, Instruction::SGET, 3u),  // Class init check not eliminated.
   };
   static const bool expected_ignore_clinit_check[] = {
       false, false, false, false, true, false, true, true, true, false
   };
 
   PrepareSFields(sfields);
-  PrepareBasicBlocks(bbs);
-  BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u);
-  catch_handler->catch_entry = true;
-  // Add successor block info to the check block.
-  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));
-  successor_block_info->block = catch_handler->id;
-  check_bb->successor_blocks.push_back(successor_block_info);
+  PrepareCatch();
   PrepareMIRs(mirs);
   PerformClassInitCheckElimination();
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
@@ -413,4 +493,189 @@
   }
 }
 
+#define DEF_IGET_IPUT(bb, opcode, vA, vB, field_info) \
+    { bb, opcode, field_info, vA, vB, 0u }
+#define DEF_AGET_APUT(bb, opcode, vA, vB, vC) \
+    { bb, opcode, 0u, vA, vB, vC }
+#define DEF_INVOKE(bb, opcode, vC) \
+    { bb, opcode, 0u, 0u, 0u, vC }
+#define DEF_OTHER1(bb, opcode, vA) \
+    { bb, opcode, 0u, vA, 0u, 0u }
+#define DEF_OTHER2(bb, opcode, vA, vB) \
+    { bb, opcode, 0u, vA, vB, 0u }
+
+TEST_F(NullCheckEliminationTest, SingleBlock) {
+  static const IFieldDef ifields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 0u, 1u },
+      { 2u, 1u, 0u, 2u },  // Object.
+  };
+  static const MIRDef mirs[] = {
+      DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 0u, 100u, 2u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 0u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 2u, 100u, 2u),  // Differs from 0u (no LVN here).
+      DEF_IGET_IPUT(3u, Instruction::IGET, 3u, 2u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 101u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 5u, 102u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 6u, 103u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 7u, 103u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IPUT, 8u, 104u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IPUT, 9u, 104u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 10u, 105u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IPUT, 11u, 105u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IPUT, 12u, 106u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 13u, 106u, 1u),
+      DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 107),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 15u, 107u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 16u, 108u, 0u),
+      DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 108),
+      DEF_AGET_APUT(3u, Instruction::AGET, 18u, 109u, 110u),
+      DEF_AGET_APUT(3u, Instruction::APUT, 19u, 109u, 111u),
+      DEF_OTHER2(3u, Instruction::ARRAY_LENGTH, 20u, 112u),
+      DEF_AGET_APUT(3u, Instruction::AGET, 21u, 112u, 113u),
+      DEF_OTHER1(3u, Instruction::MONITOR_ENTER, 114u),
+      DEF_OTHER1(3u, Instruction::MONITOR_EXIT, 114u),
+  };
+  static const bool expected_ignore_null_check[] = {
+      false, false, true, false /* Not doing LVN. */,
+      false, true /* Set before running NCE. */,
+      false, true,  // IGET, IGET
+      false, true,  // IPUT, IPUT
+      false, true,  // IGET, IPUT
+      false, true,  // IPUT, IGET
+      false, true,  // INVOKE, IGET
+      false, true,  // IGET, INVOKE
+      false, true,  // AGET, APUT
+      false, true,  // ARRAY_LENGTH, AGET
+      false, true,  // MONITOR_ENTER, MONITOR_EXIT
+  };
+
+  PrepareIFields(ifields);
+  PrepareSingleBlock();
+  PrepareMIRs(mirs);
+
+  // Mark IGET 5u as null-checked to test that NCE doesn't clear this flag.
+  mirs_[5u].optimization_flags |= MIR_IGNORE_NULL_CHECK;
+
+  PerformNullCheckElimination();
+  ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_ignore_null_check[i],
+              (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+  }
+}
+
+TEST_F(NullCheckEliminationTest, Diamond) {
+  static const IFieldDef ifields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 0u, 1u },
+      { 2u, 1u, 0u, 2u },  // int[].
+  };
+  static const MIRDef mirs[] = {
+      // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
+      DEF_IGET_IPUT(3u, Instruction::IPUT, 0u, 100u, 0u),
+      DEF_IGET_IPUT(6u, Instruction::IGET, 1u, 100u, 1u),  // Eliminated (BB #3 dominates #6).
+      DEF_IGET_IPUT(3u, Instruction::IGET, 2u, 101u, 0u),
+      DEF_IGET_IPUT(4u, Instruction::IPUT, 3u, 101u, 0u),  // Eliminated (BB #3 dominates #4).
+      DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u),
+      DEF_IGET_IPUT(5u, Instruction::IPUT, 5u, 102u, 1u),  // Eliminated (BB #3 dominates #5).
+      DEF_IGET_IPUT(4u, Instruction::IPUT, 6u, 103u, 0u),
+      DEF_IGET_IPUT(6u, Instruction::IPUT, 7u, 103u, 1u),  // Not eliminated (going through BB #5).
+      DEF_IGET_IPUT(5u, Instruction::IGET, 8u, 104u, 1u),
+      DEF_IGET_IPUT(6u, Instruction::IGET, 9u, 104u, 0u),  // Not eliminated (going through BB #4).
+      DEF_INVOKE(4u, Instruction::INVOKE_DIRECT, 105u),
+      DEF_IGET_IPUT(5u, Instruction::IGET, 11u, 105u, 1u),
+      DEF_IGET_IPUT(6u, Instruction::IPUT, 12u, 105u, 0u),  // Eliminated.
+      DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 13u, 106u, 2u),
+      DEF_OTHER1(3u, Instruction::IF_EQZ, 13u),            // Last insn in the BB #3.
+      DEF_OTHER2(5u, Instruction::NEW_ARRAY, 13u, 107u),
+      DEF_AGET_APUT(6u, Instruction::AGET, 16u, 13u, 108u),  // Eliminated.
+  };
+  static const bool expected_ignore_null_check[] = {
+      false, true,   // BB #3 IPUT, BB #6 IGET
+      false, true,   // BB #3 IGET, BB #4 IPUT
+      false, true,   // BB #3 IGET, BB #5 IPUT
+      false, false,  // BB #4 IPUT, BB #6 IPUT
+      false, false,  // BB #5 IGET, BB #6 IGET
+      false, false, true,  // BB #4 INVOKE, BB #5 IGET, BB #6 IPUT
+      false, false,  // BB #3 IGET_OBJECT & IF_EQZ
+      false, true,   // BB #5 NEW_ARRAY, BB #6 AGET
+  };
+
+  PrepareIFields(ifields);
+  PrepareDiamond();
+  PrepareMIRs(mirs);
+  PerformNullCheckElimination();
+  ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_ignore_null_check[i],
+              (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+  }
+}
+
+TEST_F(NullCheckEliminationTest, Loop) {
+  static const IFieldDef ifields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 1u, 1u },
+  };
+  static const MIRDef mirs[] = {
+      DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u),
+      DEF_IGET_IPUT(4u, Instruction::IGET, 1u, 101u, 0u),
+      DEF_IGET_IPUT(5u, Instruction::IGET, 2u, 100u, 1u),  // Eliminated.
+      DEF_IGET_IPUT(5u, Instruction::IGET, 3u, 101u, 1u),  // Eliminated.
+      DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u),
+      DEF_IGET_IPUT(4u, Instruction::IGET, 5u, 102u, 1u),  // Not eliminated (MOVE_OBJECT_16).
+      DEF_OTHER2(4u, Instruction::MOVE_OBJECT_16, 102u, 103u),
+  };
+  static const bool expected_ignore_null_check[] = {
+      false, false, true, true,
+      false, false, false,
+  };
+
+  PrepareIFields(ifields);
+  PrepareLoop();
+  PrepareMIRs(mirs);
+  PerformNullCheckElimination();
+  ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_ignore_null_check[i],
+              (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+  }
+}
+
+TEST_F(NullCheckEliminationTest, Catch) {
+  static const IFieldDef ifields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 1u, 1u },
+  };
+  static const MIRDef mirs[] = {
+      DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u),  // Before the exception edge.
+      DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 101u, 0u),  // Before the exception edge.
+      DEF_IGET_IPUT(4u, Instruction::IGET, 2u, 102u, 0u),  // After the exception edge.
+      DEF_IGET_IPUT(4u, Instruction::IGET, 3u, 103u, 0u),  // After the exception edge.
+      DEF_IGET_IPUT(5u, Instruction::IGET, 4u, 100u, 1u),  // In catch handler; eliminated.
+      DEF_IGET_IPUT(5u, Instruction::IGET, 5u, 102u, 1u),  // In catch handler; not eliminated.
+      DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 100u, 0u),  // Null check eliminated.
+      DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 101u, 1u),  // Null check eliminated.
+      DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 102u, 0u),  // Null check eliminated.
+      DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 103u, 1u),  // Null check not eliminated.
+  };
+  static const bool expected_ignore_null_check[] = {
+      false, false, false, false, true, false, true, true, true, false
+  };
+
+  PrepareIFields(ifields);
+  PrepareCatch();
+  PrepareMIRs(mirs);
+  PerformNullCheckElimination();
+  ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_ignore_null_check[i],
+              (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+  }
+}
+
+// Undefine MIR_DEF for null check elimination.
+#undef MIR_DEF
+
 }  // namespace art
diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h
index c377426..e349eed 100644
--- a/compiler/dex/pass.h
+++ b/compiler/dex/pass.h
@@ -85,6 +85,9 @@
     // Unused parameter.
     UNUSED(data);
 
+    // Passes that do all their work in Start() or End() should not allow useless node iteration.
+    DCHECK(false) << "Unsupported default Worker() used for " << GetName();
+
     // BasicBlock did not change.
     return false;
   }
diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc
index cd3ffd4..a2bf8b4 100644
--- a/compiler/dex/pass_driver_me_opts.cc
+++ b/compiler/dex/pass_driver_me_opts.cc
@@ -20,6 +20,7 @@
 #include "dataflow_iterator.h"
 #include "dataflow_iterator-inl.h"
 #include "pass_driver_me_opts.h"
+#include "post_opt_passes.h"
 
 namespace art {
 
@@ -35,13 +36,15 @@
 const Pass* const PassDriver<PassDriverMEOpts>::g_passes[] = {
   GetPassInstance<CacheFieldLoweringInfo>(),
   GetPassInstance<CacheMethodLoweringInfo>(),
-  GetPassInstance<SpecialMethodInliner>(),
-  GetPassInstance<CodeLayout>(),
-  GetPassInstance<NullCheckElimination>(),
-  GetPassInstance<TypeInference>(),
+  GetPassInstance<CalculatePredecessors>(),
+  GetPassInstance<DFSOrders>(),
   GetPassInstance<ClassInitCheckElimination>(),
-  GetPassInstance<GlobalValueNumberingPass>(),
+  GetPassInstance<SpecialMethodInliner>(),
+  GetPassInstance<NullCheckElimination>(),
   GetPassInstance<BBCombine>(),
+  GetPassInstance<CodeLayout>(),
+  GetPassInstance<TypeInference>(),
+  GetPassInstance<GlobalValueNumberingPass>(),
   GetPassInstance<BBOptimizations>(),
 };
 
diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc
index 4acab6c..e6238e9 100644
--- a/compiler/dex/pass_driver_me_post_opt.cc
+++ b/compiler/dex/pass_driver_me_post_opt.cc
@@ -33,7 +33,6 @@
 const Pass* const PassDriver<PassDriverMEPostOpt>::g_passes[] = {
   GetPassInstance<InitializeData>(),
   GetPassInstance<ClearPhiInstructions>(),
-  GetPassInstance<CalculatePredecessors>(),
   GetPassInstance<DFSOrders>(),
   GetPassInstance<BuildDomination>(),
   GetPassInstance<TopologicalSortOrders>(),
diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h
index e7805ba..7b84ba8 100644
--- a/compiler/dex/post_opt_passes.h
+++ b/compiler/dex/post_opt_passes.h
@@ -30,7 +30,7 @@
  */
 class InitializeData : public PassME {
  public:
-  InitializeData() : PassME("InitializeData") {
+  InitializeData() : PassME("InitializeData", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -76,7 +76,7 @@
  */
 class CalculatePredecessors : public PassME {
  public:
-  CalculatePredecessors() : PassME("CalculatePredecessors") {
+  CalculatePredecessors() : PassME("CalculatePredecessors", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const;
@@ -88,7 +88,14 @@
  */
 class DFSOrders : public PassME {
  public:
-  DFSOrders() : PassME("DFSOrders") {
+  DFSOrders() : PassME("DFSOrders", kNoNodes) {
+  }
+
+  bool Gate(const PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return !c_unit->mir_graph->DfsOrdersUpToDate();
   }
 
   void Start(PassDataHolder* data) const {
@@ -105,7 +112,7 @@
  */
 class BuildDomination : public PassME {
  public:
-  BuildDomination() : PassME("BuildDomination") {
+  BuildDomination() : PassME("BuildDomination", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -133,7 +140,7 @@
  */
 class TopologicalSortOrders : public PassME {
  public:
-  TopologicalSortOrders() : PassME("TopologicalSortOrders") {
+  TopologicalSortOrders() : PassME("TopologicalSortOrders", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -150,7 +157,7 @@
  */
 class DefBlockMatrix : public PassME {
  public:
-  DefBlockMatrix() : PassME("DefBlockMatrix") {
+  DefBlockMatrix() : PassME("DefBlockMatrix", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -167,7 +174,7 @@
  */
 class CreatePhiNodes : public PassME {
  public:
-  CreatePhiNodes() : PassME("CreatePhiNodes") {
+  CreatePhiNodes() : PassME("CreatePhiNodes", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -185,7 +192,7 @@
 
 class ClearVisitedFlag : public PassME {
  public:
-  ClearVisitedFlag() : PassME("ClearVisitedFlag") {
+  ClearVisitedFlag() : PassME("ClearVisitedFlag", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -202,7 +209,7 @@
  */
 class SSAConversion : public PassME {
  public:
-  SSAConversion() : PassME("SSAConversion") {
+  SSAConversion() : PassME("SSAConversion", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -241,7 +248,7 @@
  */
 class PerformInitRegLocations : public PassME {
  public:
-  PerformInitRegLocations() : PassME("PerformInitRegLocation") {
+  PerformInitRegLocations() : PassME("PerformInitRegLocation", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -286,7 +293,7 @@
  */
 class FreeData : public PassME {
  public:
-  FreeData() : PassME("FreeData") {
+  FreeData() : PassME("FreeData", kNoNodes) {
   }
 
   void End(PassDataHolder* data) const {
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index 4388041..412f85d 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -66,8 +66,9 @@
 }
 
 void MIRGraph::RecordDFSOrders(BasicBlock* block) {
-  DCHECK(temp_scoped_alloc_.get() != nullptr);
-  ScopedArenaVector<BasicBlock*> succ(temp_scoped_alloc_->Adapter());
+  ScopedArenaAllocator allocator(&cu_->arena_stack);
+  ScopedArenaVector<BasicBlock*> succ(allocator.Adapter());
+  succ.reserve(GetNumBlocks());
   MarkPreOrder(block);
   succ.push_back(block);
   while (!succ.empty()) {
@@ -107,10 +108,11 @@
     AllNodesIterator iter(this);
     for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
       if (!bb->visited) {
-        bb->Hide(cu_);
+        bb->Hide(this);
       }
     }
   }
+  dfs_orders_up_to_date_ = true;
 }
 
 /*
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 29dbd8b..408e13e 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -36,7 +36,7 @@
   const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
   DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
   DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
-  block_labels_.SetSize(blocks.Size());
+  Initialize();
 
   DCHECK_EQ(frame_size_, kUninitializedFrameSize);
   if (!is_leaf) {
@@ -54,7 +54,7 @@
   HGraphVisitor* instruction_visitor = GetInstructionVisitor();
   for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
     HBasicBlock* block = blocks.Get(i);
-    Bind(GetLabelOf(block));
+    Bind(block);
     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* current = it.Current();
       current->Accept(location_builder);
@@ -76,13 +76,13 @@
   const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
   DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
   DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
-  block_labels_.SetSize(blocks.Size());
+  Initialize();
 
   GenerateFrameEntry();
   HGraphVisitor* instruction_visitor = GetInstructionVisitor();
   for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
     HBasicBlock* block = blocks.Get(i);
-    Bind(GetLabelOf(block));
+    Bind(block);
     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* current = it.Current();
       current->Accept(instruction_visitor);
@@ -273,10 +273,6 @@
   return current->GetBlockId() + 1 == next->GetBlockId();
 }
 
-Label* CodeGenerator::GetLabelOf(HBasicBlock* block) const {
-  return block_labels_.GetRawStorage() + block->GetBlockId();
-}
-
 CodeGenerator* CodeGenerator::Create(ArenaAllocator* allocator,
                                      HGraph* graph,
                                      InstructionSet instruction_set) {
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 4eba791..7aaf991 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -24,13 +24,13 @@
 #include "memory_region.h"
 #include "nodes.h"
 #include "stack_map_stream.h"
-#include "utils/assembler.h"
 
 namespace art {
 
 static size_t constexpr kVRegSize = 4;
 static size_t constexpr kUninitializedFrameSize = 0;
 
+class Assembler;
 class CodeGenerator;
 class DexCompilationUnit;
 class SrcMap;
@@ -53,18 +53,12 @@
 
 class SlowPathCode : public ArenaObject {
  public:
-  SlowPathCode() : entry_label_(), exit_label_() {}
+  SlowPathCode() {}
   virtual ~SlowPathCode() {}
 
-  Label* GetEntryLabel() { return &entry_label_; }
-  Label* GetExitLabel() { return &exit_label_; }
-
   virtual void EmitNativeCode(CodeGenerator* codegen) = 0;
 
  private:
-  Label entry_label_;
-  Label exit_label_;
-
   DISALLOW_COPY_AND_ASSIGN(SlowPathCode);
 };
 
@@ -80,7 +74,6 @@
 
   HGraph* GetGraph() const { return graph_; }
 
-  Label* GetLabelOf(HBasicBlock* block) const;
   bool GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const;
 
   size_t GetStackSlotOfParameter(HParameterValue* parameter) const {
@@ -90,9 +83,10 @@
         + parameter->GetIndex() * kVRegSize;
   }
 
+  virtual void Initialize() = 0;
   virtual void GenerateFrameEntry() = 0;
   virtual void GenerateFrameExit() = 0;
-  virtual void Bind(Label* label) = 0;
+  virtual void Bind(HBasicBlock* block) = 0;
   virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0;
   virtual HGraphVisitor* GetLocationBuilder() = 0;
   virtual HGraphVisitor* GetInstructionVisitor() = 0;
@@ -167,7 +161,6 @@
         number_of_fpu_registers_(number_of_fpu_registers),
         number_of_register_pairs_(number_of_register_pairs),
         graph_(graph),
-        block_labels_(graph->GetArena(), 0),
         pc_infos_(graph->GetArena(), 32),
         slow_paths_(graph->GetArena(), 8),
         is_leaf_(true),
@@ -205,8 +198,6 @@
 
   HGraph* const graph_;
 
-  // Labels for each block that will be compiled.
-  GrowableArray<Label> block_labels_;
   GrowableArray<PcInfo> pc_infos_;
   GrowableArray<SlowPathCode*> slow_paths_;
 
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 9be7802..cdee845 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -60,7 +60,21 @@
 
 #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
 
-class NullCheckSlowPathARM : public SlowPathCode {
+class SlowPathCodeARM : public SlowPathCode {
+ public:
+  SlowPathCodeARM() : entry_label_(), exit_label_() {}
+
+  Label* GetEntryLabel() { return &entry_label_; }
+  Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+  Label entry_label_;
+  Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM);
+};
+
+class NullCheckSlowPathARM : public SlowPathCodeARM {
  public:
   explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
 
@@ -77,7 +91,7 @@
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
 };
 
-class StackOverflowCheckSlowPathARM : public SlowPathCode {
+class StackOverflowCheckSlowPathARM : public SlowPathCodeARM {
  public:
   StackOverflowCheckSlowPathARM() {}
 
@@ -91,12 +105,13 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
 };
 
-class SuspendCheckSlowPathARM : public SlowPathCode {
+class SuspendCheckSlowPathARM : public SlowPathCodeARM {
  public:
   explicit SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
       : instruction_(instruction), successor_(successor) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
     __ Bind(GetEntryLabel());
     codegen->SaveLiveRegisters(instruction_->GetLocations());
     int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pTestSuspend).Int32Value();
@@ -107,7 +122,7 @@
     if (successor_ == nullptr) {
       __ b(GetReturnLabel());
     } else {
-      __ b(codegen->GetLabelOf(successor_));
+      __ b(arm_codegen->GetLabelOf(successor_));
     }
   }
 
@@ -127,7 +142,7 @@
   DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM);
 };
 
-class BoundsCheckSlowPathARM : public SlowPathCode {
+class BoundsCheckSlowPathARM : public SlowPathCodeARM {
  public:
   BoundsCheckSlowPathARM(HBoundsCheck* instruction,
                          Location index_location,
@@ -137,7 +152,7 @@
         length_location_(length_location) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen);
+    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
     __ Bind(GetEntryLabel());
     InvokeRuntimeCallingConvention calling_convention;
     arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
@@ -205,6 +220,7 @@
 
 CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
     : CodeGenerator(graph, kNumberOfCoreRegisters, kNumberOfDRegisters, kNumberOfRegisterPairs),
+      block_labels_(graph->GetArena(), 0),
       location_builder_(graph, this),
       instruction_visitor_(graph, this),
       move_resolver_(graph->GetArena(), this),
@@ -313,7 +329,7 @@
   bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
   if (!skip_overflow_check) {
     if (kExplicitStackOverflowCheck) {
-      SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM();
+      SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM();
       AddSlowPath(slow_path);
 
       __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset<kArmWordSize>().Int32Value());
@@ -339,8 +355,8 @@
   __ PopList(1 << PC | 1 << R6 | 1 << R7);
 }
 
-void CodeGeneratorARM::Bind(Label* label) {
-  __ Bind(label);
+void CodeGeneratorARM::Bind(HBasicBlock* block) {
+  __ Bind(GetLabelOf(block));
 }
 
 Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const {
@@ -641,36 +657,51 @@
 
 void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
   HInstruction* cond = if_instr->InputAt(0);
-  if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
-    // Condition has been materialized, compare the output to 0
-    DCHECK(if_instr->GetLocations()->InAt(0).IsRegister());
-    __ cmp(if_instr->GetLocations()->InAt(0).As<Register>(),
-           ShifterOperand(0));
-    __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), NE);
-  } else {
-    // Condition has not been materialized, use its inputs as the comparison and its
-    // condition as the branch condition.
-    LocationSummary* locations = cond->GetLocations();
-    if (locations->InAt(1).IsRegister()) {
-      __ cmp(locations->InAt(0).As<Register>(),
-             ShifterOperand(locations->InAt(1).As<Register>()));
-    } else {
-      DCHECK(locations->InAt(1).IsConstant());
-      int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
-      ShifterOperand operand;
-      if (ShifterOperand::CanHoldArm(value, &operand)) {
-        __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(value));
-      } else {
-        Register temp = IP;
-        __ LoadImmediate(temp, value);
-        __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(temp));
+  if (cond->IsIntConstant()) {
+    // Constant condition, statically compared against 1.
+    int32_t cond_value = cond->AsIntConstant()->GetValue();
+    if (cond_value == 1) {
+      if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                     if_instr->IfTrueSuccessor())) {
+        __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
       }
+      return;
+    } else {
+      DCHECK_EQ(cond_value, 0);
     }
-    __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()),
-         ARMCondition(cond->AsCondition()->GetCondition()));
+  } else {
+    if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
+      // Condition has been materialized, compare the output to 0
+      DCHECK(if_instr->GetLocations()->InAt(0).IsRegister());
+      __ cmp(if_instr->GetLocations()->InAt(0).As<Register>(),
+             ShifterOperand(0));
+      __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), NE);
+    } else {
+      // Condition has not been materialized, use its inputs as the
+      // comparison and its condition as the branch condition.
+      LocationSummary* locations = cond->GetLocations();
+      if (locations->InAt(1).IsRegister()) {
+        __ cmp(locations->InAt(0).As<Register>(),
+               ShifterOperand(locations->InAt(1).As<Register>()));
+      } else {
+        DCHECK(locations->InAt(1).IsConstant());
+        int32_t value =
+            locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+        ShifterOperand operand;
+        if (ShifterOperand::CanHoldArm(value, &operand)) {
+          __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(value));
+        } else {
+          Register temp = IP;
+          __ LoadImmediate(temp, value);
+          __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(temp));
+        }
+      }
+      __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()),
+           ARMCondition(cond->AsCondition()->GetCondition()));
+    }
   }
-
-  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
+  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                 if_instr->IfFalseSuccessor())) {
     __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
   }
 }
@@ -810,6 +841,7 @@
 }
 
 void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) {
+  // Will be generated at use site.
 }
 
 void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
@@ -1349,7 +1381,7 @@
 }
 
 void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
+  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
@@ -1595,7 +1627,7 @@
 
 void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
+  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
       instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 874db0f..7c063f1 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -140,7 +140,7 @@
 
   virtual void GenerateFrameEntry() OVERRIDE;
   virtual void GenerateFrameExit() OVERRIDE;
-  virtual void Bind(Label* label) OVERRIDE;
+  virtual void Bind(HBasicBlock* block) OVERRIDE;
   virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
   virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
   virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
@@ -187,7 +187,17 @@
   // Emit a write barrier.
   void MarkGCCard(Register temp, Register card, Register object, Register value);
 
+  Label* GetLabelOf(HBasicBlock* block) const {
+    return block_labels_.GetRawStorage() + block->GetBlockId();
+  }
+
+  virtual void Initialize() OVERRIDE {
+    block_labels_.SetSize(GetGraph()->GetBlocks().Size());
+  }
+
  private:
+  // Labels for each block that will be compiled.
+  GrowableArray<Label> block_labels_;
   LocationsBuilderARM location_builder_;
   InstructionCodeGeneratorARM instruction_visitor_;
   ParallelMoveResolverARM move_resolver_;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 34fa46e..98d3ad4 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -56,7 +56,21 @@
 
 #define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())->
 
-class NullCheckSlowPathX86 : public SlowPathCode {
+class SlowPathCodeX86 : public SlowPathCode {
+ public:
+  SlowPathCodeX86() : entry_label_(), exit_label_() {}
+
+  Label* GetEntryLabel() { return &entry_label_; }
+  Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+  Label entry_label_;
+  Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86);
+};
+
+class NullCheckSlowPathX86 : public SlowPathCodeX86 {
  public:
   explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {}
 
@@ -71,7 +85,7 @@
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86);
 };
 
-class StackOverflowCheckSlowPathX86 : public SlowPathCode {
+class StackOverflowCheckSlowPathX86 : public SlowPathCodeX86 {
  public:
   StackOverflowCheckSlowPathX86() {}
 
@@ -86,7 +100,7 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86);
 };
 
-class BoundsCheckSlowPathX86 : public SlowPathCode {
+class BoundsCheckSlowPathX86 : public SlowPathCodeX86 {
  public:
   BoundsCheckSlowPathX86(HBoundsCheck* instruction,
                          Location index_location,
@@ -94,7 +108,7 @@
       : instruction_(instruction), index_location_(index_location), length_location_(length_location) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen);
+    CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
     __ Bind(GetEntryLabel());
     InvokeRuntimeCallingConvention calling_convention;
     x86_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
@@ -111,12 +125,13 @@
   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86);
 };
 
-class SuspendCheckSlowPathX86 : public SlowPathCode {
+class SuspendCheckSlowPathX86 : public SlowPathCodeX86 {
  public:
   explicit SuspendCheckSlowPathX86(HSuspendCheck* instruction, HBasicBlock* successor)
       : instruction_(instruction), successor_(successor) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
     __ Bind(GetEntryLabel());
     codegen->SaveLiveRegisters(instruction_->GetLocations());
     __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pTestSuspend)));
@@ -125,7 +140,7 @@
     if (successor_ == nullptr) {
       __ jmp(GetReturnLabel());
     } else {
-      __ jmp(codegen->GetLabelOf(successor_));
+      __ jmp(x86_codegen->GetLabelOf(successor_));
     }
   }
 
@@ -177,6 +192,7 @@
 
 CodeGeneratorX86::CodeGeneratorX86(HGraph* graph)
     : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfXmmRegisters, kNumberOfRegisterPairs),
+      block_labels_(graph->GetArena(), 0),
       location_builder_(graph, this),
       instruction_visitor_(graph, this),
       move_resolver_(graph->GetArena(), this) {}
@@ -276,7 +292,7 @@
   __ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
 
   if (!skip_overflow_check && kExplicitStackOverflowCheck) {
-    SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86();
+    SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86();
     AddSlowPath(slow_path);
 
     __ fs()->cmpl(ESP, Address::Absolute(Thread::StackEndOffset<kX86WordSize>()));
@@ -290,8 +306,8 @@
   __ addl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
 }
 
-void CodeGeneratorX86::Bind(Label* label) {
-  __ Bind(label);
+void CodeGeneratorX86::Bind(HBasicBlock* block) {
+  __ Bind(GetLabelOf(block));
 }
 
 void InstructionCodeGeneratorX86::LoadCurrentMethod(Register reg) {
@@ -577,42 +593,60 @@
 
 void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) {
   HInstruction* cond = if_instr->InputAt(0);
-  bool materialized = !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
-  // Moves do not affect the eflags register, so if the condition is evaluated
-  // just before the if, we don't need to evaluate it again.
-  bool eflags_set = cond->IsCondition()
-      && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr);
-  if (materialized) {
-    if (!eflags_set) {
-      // Materialized condition, compare against 0.
-      Location lhs = if_instr->GetLocations()->InAt(0);
-      if (lhs.IsRegister()) {
-        __ cmpl(lhs.As<Register>(), Immediate(0));
-      } else {
-        __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0));
+  if (cond->IsIntConstant()) {
+    // Constant condition, statically compared against 1.
+    int32_t cond_value = cond->AsIntConstant()->GetValue();
+    if (cond_value == 1) {
+      if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                     if_instr->IfTrueSuccessor())) {
+        __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
       }
-      __ j(kNotEqual,  codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      return;
     } else {
+      DCHECK_EQ(cond_value, 0);
+    }
+  } else {
+    bool materialized =
+        !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
+    // Moves do not affect the eflags register, so if the condition is
+    // evaluated just before the if, we don't need to evaluate it
+    // again.
+    bool eflags_set = cond->IsCondition()
+        && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr);
+    if (materialized) {
+      if (!eflags_set) {
+        // Materialized condition, compare against 0.
+        Location lhs = if_instr->GetLocations()->InAt(0);
+        if (lhs.IsRegister()) {
+          __ cmpl(lhs.As<Register>(), Immediate(0));
+        } else {
+          __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0));
+        }
+        __ j(kNotEqual,  codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      } else {
+        __ j(X86Condition(cond->AsCondition()->GetCondition()),
+             codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      }
+    } else {
+      Location lhs = cond->GetLocations()->InAt(0);
+      Location rhs = cond->GetLocations()->InAt(1);
+      // LHS is guaranteed to be in a register (see
+      // LocationsBuilderX86::VisitCondition).
+      if (rhs.IsRegister()) {
+        __ cmpl(lhs.As<Register>(), rhs.As<Register>());
+      } else if (rhs.IsConstant()) {
+        HIntConstant* instruction = rhs.GetConstant()->AsIntConstant();
+        Immediate imm(instruction->AsIntConstant()->GetValue());
+        __ cmpl(lhs.As<Register>(), imm);
+      } else {
+        __ cmpl(lhs.As<Register>(), Address(ESP, rhs.GetStackIndex()));
+      }
       __ j(X86Condition(cond->AsCondition()->GetCondition()),
            codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
     }
-  } else {
-    Location lhs = cond->GetLocations()->InAt(0);
-    Location rhs = cond->GetLocations()->InAt(1);
-    // LHS is guaranteed to be in a register (see LocationsBuilderX86::VisitCondition).
-    if (rhs.IsRegister()) {
-      __ cmpl(lhs.As<Register>(), rhs.As<Register>());
-    } else if (rhs.IsConstant()) {
-      HIntConstant* instruction = rhs.GetConstant()->AsIntConstant();
-      Immediate imm(instruction->AsIntConstant()->GetValue());
-      __ cmpl(lhs.As<Register>(), imm);
-    } else {
-      __ cmpl(lhs.As<Register>(), Address(ESP, rhs.GetStackIndex()));
-    }
-    __ j(X86Condition(cond->AsCondition()->GetCondition()),
-         codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
   }
-  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
+  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                 if_instr->IfFalseSuccessor())) {
     __ jmp(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
   }
 }
@@ -747,6 +781,7 @@
 }
 
 void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) {
+  // Will be generated at use site.
 }
 
 void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) {
@@ -1356,7 +1391,7 @@
 }
 
 void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
+  SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
@@ -1658,7 +1693,7 @@
 
 void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86(
+  SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86(
       instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index a1a72a2..aa5fee0 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -142,7 +142,7 @@
 
   virtual void GenerateFrameEntry() OVERRIDE;
   virtual void GenerateFrameExit() OVERRIDE;
-  virtual void Bind(Label* label) OVERRIDE;
+  virtual void Bind(HBasicBlock* block) OVERRIDE;
   virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
   virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
   virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
@@ -189,7 +189,17 @@
   // Emit a write barrier.
   void MarkGCCard(Register temp, Register card, Register object, Register value);
 
+  Label* GetLabelOf(HBasicBlock* block) const {
+    return block_labels_.GetRawStorage() + block->GetBlockId();
+  }
+
+  virtual void Initialize() OVERRIDE {
+    block_labels_.SetSize(GetGraph()->GetBlocks().Size());
+  }
+
  private:
+  // Labels for each block that will be compiled.
+  GrowableArray<Label> block_labels_;
   LocationsBuilderX86 location_builder_;
   InstructionCodeGeneratorX86 instruction_visitor_;
   ParallelMoveResolverX86 move_resolver_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 059140d..059ff3f 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -60,7 +60,21 @@
 
 #define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
 
-class NullCheckSlowPathX86_64 : public SlowPathCode {
+class SlowPathCodeX86_64 : public SlowPathCode {
+ public:
+  SlowPathCodeX86_64() : entry_label_(), exit_label_() {}
+
+  Label* GetEntryLabel() { return &entry_label_; }
+  Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+  Label entry_label_;
+  Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86_64);
+};
+
+class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
  public:
   explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {}
 
@@ -76,7 +90,7 @@
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64);
 };
 
-class StackOverflowCheckSlowPathX86_64 : public SlowPathCode {
+class StackOverflowCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
  public:
   StackOverflowCheckSlowPathX86_64() {}
 
@@ -92,12 +106,13 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64);
 };
 
-class SuspendCheckSlowPathX86_64 : public SlowPathCode {
+class SuspendCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
  public:
   explicit SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor)
       : instruction_(instruction), successor_(successor) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
     __ Bind(GetEntryLabel());
     codegen->SaveLiveRegisters(instruction_->GetLocations());
     __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pTestSuspend), true));
@@ -106,7 +121,7 @@
     if (successor_ == nullptr) {
       __ jmp(GetReturnLabel());
     } else {
-      __ jmp(codegen->GetLabelOf(successor_));
+      __ jmp(x64_codegen->GetLabelOf(successor_));
     }
   }
 
@@ -123,7 +138,7 @@
   DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86_64);
 };
 
-class BoundsCheckSlowPathX86_64 : public SlowPathCode {
+class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
  public:
   BoundsCheckSlowPathX86_64(HBoundsCheck* instruction,
                             Location index_location,
@@ -133,7 +148,7 @@
         length_location_(length_location) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen);
+    CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
     __ Bind(GetEntryLabel());
     InvokeRuntimeCallingConvention calling_convention;
     x64_codegen->Move(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
@@ -186,6 +201,7 @@
 
 CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph)
       : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfFloatRegisters, 0),
+        block_labels_(graph->GetArena(), 0),
         location_builder_(graph, this),
         instruction_visitor_(graph, this),
         move_resolver_(graph->GetArena(), this) {}
@@ -266,7 +282,7 @@
           Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
 
   if (!skip_overflow_check && kExplicitStackOverflowCheck) {
-    SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64();
+    SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64();
     AddSlowPath(slow_path);
 
     __ gs()->cmpq(CpuRegister(RSP),
@@ -282,8 +298,8 @@
           Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
 }
 
-void CodeGeneratorX86_64::Bind(Label* label) {
-  __ Bind(label);
+void CodeGeneratorX86_64::Bind(HBasicBlock* block) {
+  __ Bind(GetLabelOf(block));
 }
 
 void InstructionCodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) {
@@ -479,40 +495,59 @@
 
 void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) {
   HInstruction* cond = if_instr->InputAt(0);
-  bool materialized = !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
-  // Moves do not affect the eflags register, so if the condition is evaluated
-  // just before the if, we don't need to evaluate it again.
-  bool eflags_set = cond->IsCondition()
-      && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr);
-  if (materialized) {
-    if (!eflags_set) {
-      // Materialized condition, compare against 0.
-      Location lhs = if_instr->GetLocations()->InAt(0);
-      if (lhs.IsRegister()) {
-        __ cmpl(lhs.As<CpuRegister>(), Immediate(0));
-      } else {
-        __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), Immediate(0));
+  if (cond->IsIntConstant()) {
+    // Constant condition, statically compared against 1.
+    int32_t cond_value = cond->AsIntConstant()->GetValue();
+    if (cond_value == 1) {
+      if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                     if_instr->IfTrueSuccessor())) {
+        __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
       }
-      __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      return;
     } else {
+      DCHECK_EQ(cond_value, 0);
+    }
+  } else {
+    bool materialized =
+        !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
+    // Moves do not affect the eflags register, so if the condition is
+    // evaluated just before the if, we don't need to evaluate it
+    // again.
+    bool eflags_set = cond->IsCondition()
+        && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr);
+    if (materialized) {
+      if (!eflags_set) {
+        // Materialized condition, compare against 0.
+        Location lhs = if_instr->GetLocations()->InAt(0);
+        if (lhs.IsRegister()) {
+          __ cmpl(lhs.As<CpuRegister>(), Immediate(0));
+        } else {
+          __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()),
+                  Immediate(0));
+        }
+        __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      } else {
+        __ j(X86_64Condition(cond->AsCondition()->GetCondition()),
+             codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      }
+    } else {
+      Location lhs = cond->GetLocations()->InAt(0);
+      Location rhs = cond->GetLocations()->InAt(1);
+      if (rhs.IsRegister()) {
+        __ cmpl(lhs.As<CpuRegister>(), rhs.As<CpuRegister>());
+      } else if (rhs.IsConstant()) {
+        __ cmpl(lhs.As<CpuRegister>(),
+                Immediate(rhs.GetConstant()->AsIntConstant()->GetValue()));
+      } else {
+        __ cmpl(lhs.As<CpuRegister>(),
+                Address(CpuRegister(RSP), rhs.GetStackIndex()));
+      }
       __ j(X86_64Condition(cond->AsCondition()->GetCondition()),
            codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
     }
-  } else {
-    Location lhs = cond->GetLocations()->InAt(0);
-    Location rhs = cond->GetLocations()->InAt(1);
-    if (rhs.IsRegister()) {
-      __ cmpl(lhs.As<CpuRegister>(), rhs.As<CpuRegister>());
-    } else if (rhs.IsConstant()) {
-      __ cmpl(lhs.As<CpuRegister>(),
-              Immediate(rhs.GetConstant()->AsIntConstant()->GetValue()));
-    } else {
-      __ cmpl(lhs.As<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
-    }
-    __ j(X86_64Condition(cond->AsCondition()->GetCondition()),
-         codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
   }
-  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
+  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                 if_instr->IfFalseSuccessor())) {
     __ jmp(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
   }
 }
@@ -679,6 +714,7 @@
 }
 
 void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant) {
+  // Will be generated at use site.
 }
 
 void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) {
@@ -688,6 +724,7 @@
 }
 
 void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) {
+  // Will be generated at use site.
 }
 
 void LocationsBuilderX86_64::VisitReturnVoid(HReturnVoid* ret) {
@@ -1233,7 +1270,7 @@
 }
 
 void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
+  SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
@@ -1505,7 +1542,7 @@
 
 void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(
+  SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(
       instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 288f3f6..5ac0189 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -144,7 +144,7 @@
 
   virtual void GenerateFrameEntry() OVERRIDE;
   virtual void GenerateFrameExit() OVERRIDE;
-  virtual void Bind(Label* label) OVERRIDE;
+  virtual void Bind(HBasicBlock* block) OVERRIDE;
   virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
   virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
   virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
@@ -188,7 +188,17 @@
   // Helper method to move a value between two locations.
   void Move(Location destination, Location source);
 
+  Label* GetLabelOf(HBasicBlock* block) const {
+    return block_labels_.GetRawStorage() + block->GetBlockId();
+  }
+
+  virtual void Initialize() OVERRIDE {
+    block_labels_.SetSize(GetGraph()->GetBlocks().Size());
+  }
+
  private:
+  // Labels for each block that will be compiled.
+  GrowableArray<Label> block_labels_;
   LocationsBuilderX86_64 location_builder_;
   InstructionCodeGeneratorX86_64 instruction_visitor_;
   ParallelMoveResolverX86_64 move_resolver_;
diff --git a/compiler/optimizing/constant_propagation_test.cc b/compiler/optimizing/constant_propagation_test.cc
index 342777a..ff44805 100644
--- a/compiler/optimizing/constant_propagation_test.cc
+++ b/compiler/optimizing/constant_propagation_test.cc
@@ -62,7 +62,7 @@
   ASSERT_EQ(expected_after_dce, actual_after_dce);
 
   SSAChecker ssa_checker(&allocator, graph);
-  ssa_checker.VisitInsertionOrder();
+  ssa_checker.Run();
   ASSERT_TRUE(ssa_checker.IsValid());
 }
 
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index fe2adc7..5655544 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -35,7 +35,10 @@
     for (i.Advance(); !i.Done(); i.Advance()) {
       HInstruction* inst = i.Current();
       DCHECK(!inst->IsControlFlow());
-      if (!inst->HasSideEffects() && !inst->HasUses() && !inst->IsSuspendCheck()) {
+      if (!inst->HasSideEffects()
+          && !inst->CanThrow()
+          && !inst->IsSuspendCheck()
+          && !inst->HasUses()) {
         block->RemoveInstruction(inst);
       }
     }
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index 245bcb2..3e0ba3a 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -47,7 +47,7 @@
   ASSERT_EQ(actual_after, expected_after);
 
   SSAChecker ssa_checker(&allocator, graph);
-  ssa_checker.VisitInsertionOrder();
+  ssa_checker.Run();
   ASSERT_TRUE(ssa_checker.IsValid());
 }
 
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 589b44a..9f40297 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -264,21 +264,38 @@
 void SSAChecker::VisitInstruction(HInstruction* instruction) {
   super_type::VisitInstruction(instruction);
 
-  // Ensure an instruction dominates all its uses (or in the present
-  // case, that all uses of an instruction (used as input) are
-  // dominated by its definition).
-  for (HInputIterator input_it(instruction); !input_it.Done();
-       input_it.Advance()) {
-    HInstruction* input = input_it.Current();
-    if (!input->Dominates(instruction)) {
+  // Ensure an instruction dominates all its uses.
+  for (HUseIterator<HInstruction> use_it(instruction->GetUses());
+       !use_it.Done(); use_it.Advance()) {
+    HInstruction* use = use_it.Current()->GetUser();
+    if (!use->IsPhi() && !instruction->Dominates(use)) {
       std::stringstream error;
-      error << "Instruction " << input->GetId()
-            << " in block " << input->GetBlock()->GetBlockId()
-            << " does not dominate use " << instruction->GetId()
-            << " in block " << current_block_->GetBlockId() << ".";
+      error << "Instruction " << instruction->GetId()
+            << " in block " << current_block_->GetBlockId()
+            << " does not dominate use " << use->GetId()
+            << " in block " << use->GetBlock()->GetBlockId() << ".";
       errors_.Insert(error.str());
     }
   }
+
+  // Ensure an instruction having an environment is dominated by the
+  // instructions contained in the environment.
+  HEnvironment* environment = instruction->GetEnvironment();
+  if (environment != nullptr) {
+    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+      HInstruction* env_instruction = environment->GetInstructionAt(i);
+      if (env_instruction != nullptr
+          && !env_instruction->Dominates(instruction)) {
+        std::stringstream error;
+        error << "Instruction " << env_instruction->GetId()
+              << " in environment of instruction " << instruction->GetId()
+              << " from block " << current_block_->GetBlockId()
+              << " does not dominate instruction " << instruction->GetId()
+              << ".";
+        errors_.Insert(error.str());
+      }
+    }
+  }
 }
 
 void SSAChecker::VisitPhi(HPhi* phi) {
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 34a770b..862f1b6 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -29,6 +29,9 @@
       allocator_(allocator),
       errors_(allocator, 0) {}
 
+  // Check the whole graph (in insertion order).
+  virtual void Run() { VisitInsertionOrder(); }
+
   // Check `block`.
   virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
 
@@ -65,6 +68,14 @@
   SSAChecker(ArenaAllocator* allocator, HGraph* graph)
     : GraphChecker(allocator, graph) {}
 
+  // Check the whole graph (in reverse post-order).
+  virtual void Run() {
+    // VisitReversePostOrder is used instead of VisitInsertionOrder,
+    // as the latter might visit dead blocks removed by the dominator
+    // computation.
+    VisitReversePostOrder();
+  }
+
   // Perform SSA form checks on `block`.
   virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
   // Loop-related checks from block `loop_header`.
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index ea06920..39def82 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -51,7 +51,7 @@
   ASSERT_NE(graph, nullptr);
 
   GraphChecker graph_checker(&allocator, graph);
-  graph_checker.VisitInsertionOrder();
+  graph_checker.Run();
   ASSERT_TRUE(graph_checker.IsValid());
 }
 
@@ -65,7 +65,7 @@
   graph->TransformToSSA();
 
   SSAChecker ssa_checker(&allocator, graph);
-  ssa_checker.VisitInsertionOrder();
+  ssa_checker.Run();
   ASSERT_TRUE(ssa_checker.IsValid());
 }
 
@@ -113,13 +113,13 @@
 
   HGraph* graph = CreateSimpleCFG(&allocator);
   GraphChecker graph_checker(&allocator, graph);
-  graph_checker.VisitInsertionOrder();
+  graph_checker.Run();
   ASSERT_TRUE(graph_checker.IsValid());
 
   // Remove the entry block from the exit block's predecessors, to create an
   // inconsistent successor/predecessor relation.
   graph->GetExitBlock()->RemovePredecessor(graph->GetEntryBlock());
-  graph_checker.VisitInsertionOrder();
+  graph_checker.Run();
   ASSERT_FALSE(graph_checker.IsValid());
 }
 
@@ -131,7 +131,7 @@
 
   HGraph* graph = CreateSimpleCFG(&allocator);
   GraphChecker graph_checker(&allocator, graph);
-  graph_checker.VisitInsertionOrder();
+  graph_checker.Run();
   ASSERT_TRUE(graph_checker.IsValid());
 
   // Remove the sole instruction of the exit block (composed of a
@@ -141,7 +141,7 @@
   HInstruction* last_inst = exit_block->GetLastInstruction();
   exit_block->RemoveInstruction(last_inst);
 
-  graph_checker.VisitInsertionOrder();
+  graph_checker.Run();
   ASSERT_FALSE(graph_checker.IsValid());
 }
 
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index a058dea..aee2177 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -553,6 +553,12 @@
   }
 }
 
+void HGraphVisitor::VisitReversePostOrder() {
+  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    VisitBasicBlock(it.Current());
+  }
+}
+
 void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) {
   for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
     it.Current()->Accept(this);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 677a4f8..e60a7e6 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -650,6 +650,7 @@
 
   virtual bool NeedsEnvironment() const { return false; }
   virtual bool IsControlFlow() const { return false; }
+  virtual bool CanThrow() const { return false; }
   bool HasSideEffects() const { return side_effects_.HasSideEffects(); }
 
   void AddUseAt(HInstruction* user, size_t index) {
@@ -1642,6 +1643,8 @@
 
   virtual bool NeedsEnvironment() const { return true; }
 
+  virtual bool CanThrow() const { return true; }
+
   uint32_t GetDexPc() const { return dex_pc_; }
 
   DECLARE_INSTRUCTION(NullCheck);
@@ -1802,6 +1805,8 @@
 
   virtual bool NeedsEnvironment() const { return true; }
 
+  virtual bool CanThrow() const { return true; }
+
   uint32_t GetDexPc() const { return dex_pc_; }
 
   DECLARE_INSTRUCTION(BoundsCheck);
@@ -1956,8 +1961,12 @@
   virtual void VisitInstruction(HInstruction* instruction) {}
   virtual void VisitBasicBlock(HBasicBlock* block);
 
+  // Visit the graph following basic block insertion order.
   void VisitInsertionOrder();
 
+  // Visit the graph following dominator tree reverse post-order.
+  void VisitReversePostOrder();
+
   HGraph* GetGraph() const { return graph_; }
 
   // Visit functions for instruction classes.
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 2e64198..14accac 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -2230,6 +2230,8 @@
     runtime.reset(StartRuntime(args.boot_image_location_,
                                args.image_location_,
                                args.instruction_set_));
+  } else {
+    MemMap::Init();
   }
 
   if (args.oat_filename_ != nullptr) {
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index fede2f8..62e0609 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -95,16 +95,29 @@
 FaultManager::~FaultManager() {
 }
 
+static void SetUpArtAction(struct sigaction* action) {
+  action->sa_sigaction = art_fault_handler;
+  sigemptyset(&action->sa_mask);
+  action->sa_flags = SA_SIGINFO | SA_ONSTACK;
+#if !defined(__APPLE__) && !defined(__mips__)
+  action->sa_restorer = nullptr;
+#endif
+}
+
+void FaultManager::EnsureArtActionInFrontOfSignalChain() {
+  if (initialized_) {
+    struct sigaction action;
+    SetUpArtAction(&action);
+    EnsureFrontOfChain(SIGSEGV, &action);
+  } else {
+    LOG(WARNING) << "Can't call " << __FUNCTION__ << " due to unitialized fault manager";
+  }
+}
 
 void FaultManager::Init() {
   CHECK(!initialized_);
   struct sigaction action;
-  action.sa_sigaction = art_fault_handler;
-  sigemptyset(&action.sa_mask);
-  action.sa_flags = SA_SIGINFO | SA_ONSTACK;
-#if !defined(__APPLE__) && !defined(__mips__)
-  action.sa_restorer = nullptr;
-#endif
+  SetUpArtAction(&action);
 
   // Set our signal handler now.
   int e = sigaction(SIGSEGV, &action, &oldaction_);
@@ -138,7 +151,6 @@
   //
   // If malloc calls abort, it will be holding its lock.
   // If the handler tries to call malloc, it will deadlock.
-
   VLOG(signals) << "Handling fault";
   if (IsInGeneratedCode(info, context, true)) {
     VLOG(signals) << "in generated code, looking for handler";
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 8b66a6f..adac4c2 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -45,6 +45,7 @@
 
   // Unclaim signals and delete registered handlers.
   void Shutdown();
+  void EnsureArtActionInFrontOfSignalChain();
 
   void HandleFault(int sig, siginfo_t* info, void* context);
   void HandleNestedSignal(int sig, siginfo_t* info, void* context);
@@ -128,7 +129,6 @@
   DISALLOW_COPY_AND_ASSIGN(JavaStackTraceHandler);
 };
 
-
 // Statically allocated so the the signal handler can Get access to it.
 extern FaultManager fault_manager;
 
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 1444d97..fed8bf0 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -21,6 +21,7 @@
 #include "base/mutex.h"
 #include "base/stl_util.h"
 #include "check_jni.h"
+#include "fault_handler.h"
 #include "indirect_reference_table-inl.h"
 #include "mirror/art_method.h"
 #include "mirror/class-inl.h"
@@ -688,6 +689,10 @@
     JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
     int version = (*jni_on_load)(this, nullptr);
 
+    if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
+      fault_manager.EnsureArtActionInFrontOfSignalChain();
+    }
+
     self->SetClassLoaderOverride(old_class_loader.get());
 
     if (version == JNI_ERR) {
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index e098e11..70754f2 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -30,6 +30,7 @@
 #include "base/stl_util.h"
 #include "class_linker-inl.h"
 #include "dex_file-inl.h"
+#include "fault_handler.h"
 #include "gc_root.h"
 #include "gc/accounting/card_table-inl.h"
 #include "indirect_reference_table-inl.h"
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 35411e2..646830a 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -34,6 +34,7 @@
 #include "monitor.h"
 #include "scoped_thread_state_change.h"
 #include "thread.h"
+#include "trace.h"
 #include "utils.h"
 #include "well_known_classes.h"
 
@@ -877,6 +878,9 @@
   // suspend and so on, must happen at this point, and not in ~Thread.
   self->Destroy();
 
+  // If tracing, remember thread id and name before thread exits.
+  Trace::StoreExitingThreadInfo(self);
+
   uint32_t thin_lock_id = self->GetThreadId();
   while (self != nullptr) {
     // Remove and delete the Thread* while holding the thread_list_lock_ and
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 027f62d..91a37fd 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -706,9 +706,21 @@
 
 void Trace::DumpThreadList(std::ostream& os) {
   Thread* self = Thread::Current();
+  for (auto it : exited_threads_) {
+    os << it.first << "\t" << it.second << "\n";
+  }
   Locks::thread_list_lock_->AssertNotHeld(self);
   MutexLock mu(self, *Locks::thread_list_lock_);
   Runtime::Current()->GetThreadList()->ForEach(DumpThread, &os);
 }
 
+void Trace::StoreExitingThreadInfo(Thread* thread) {
+  MutexLock mu(thread, *Locks::trace_lock_);
+  if (the_trace_ != nullptr) {
+    std::string name;
+    thread->GetThreadName(name);
+    the_trace_->exited_threads_.Put(thread->GetTid(), name);
+  }
+}
+
 }  // namespace art
diff --git a/runtime/trace.h b/runtime/trace.h
index 45a02da..ead1c29 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -104,6 +104,8 @@
   static std::vector<mirror::ArtMethod*>* AllocStackTrace();
   // Clear and store an old stack trace for later use.
   static void FreeStackTrace(std::vector<mirror::ArtMethod*>* stack_trace);
+  // Save id and name of a thread before it exits.
+  static void StoreExitingThreadInfo(Thread* thread);
 
  private:
   explicit Trace(File* trace_file, int buffer_size, int flags, bool sampling_enabled);
@@ -166,6 +168,9 @@
   // Did we overflow the buffer recording traces?
   bool overflow_;
 
+  // Map of thread ids and names that have already exited.
+  SafeMap<pid_t, std::string> exited_threads_;
+
   DISALLOW_COPY_AND_ASSIGN(Trace);
 };
 
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index c5015e8..601e321 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -35,6 +35,8 @@
 
 namespace art {
 
+typedef int (*SigActionFnPtr)(int, const struct sigaction*, struct sigaction*);
+
 class SignalAction {
  public:
   SignalAction() : claimed_(false), uses_old_style_(false) {
@@ -147,7 +149,20 @@
   }
 }
 
-// These functions are C linkage since they replace the functions in libc.
+extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
+  CheckSignalValid(signal);
+  // Read the current action without looking at the chain, it should be the expected action.
+  SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
+  struct sigaction current_action;
+  linked_sigaction(signal, nullptr, &current_action);
+  // If the sigactions don't match then we put the current action on the chain and make ourself as
+  // the main action.
+  if (current_action.sa_sigaction != expected_action->sa_sigaction) {
+    log("Warning: Unexpected sigaction action found %p\n", current_action.sa_sigaction);
+    user_sigactions[signal].Claim(current_action);
+    linked_sigaction(signal, expected_action, nullptr);
+  }
+}
 
 extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
   // If this signal has been claimed as a signal chain, record the user's
@@ -179,9 +194,7 @@
     log("Unable to find next sigaction in signal chain");
     abort();
   }
-
-  typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
-  SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
+  SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
   return linked_sigaction(signal, new_action, old_action);
 }
 
@@ -287,5 +300,6 @@
   }
   initialized = true;
 }
+
 }   // namespace art
 
diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h
index 0de0d08..79b76a7 100644
--- a/sigchainlib/sigchain.h
+++ b/sigchainlib/sigchain.h
@@ -19,6 +19,8 @@
 
 #include <signal.h>
 
+namespace art {
+
 extern "C" void InitializeSignalChain();
 
 extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction);
@@ -27,4 +29,8 @@
 
 extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context);
 
+extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action);
+
+}  // namespace art
+
 #endif  // ART_SIGCHAINLIB_SIGCHAIN_H_
diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc
index 7176f05..fbc8c3f 100644
--- a/sigchainlib/sigchain_dummy.cc
+++ b/sigchainlib/sigchain_dummy.cc
@@ -57,3 +57,8 @@
   log("InitializeSignalChain is not exported by the main executable.");
   abort();
 }
+
+extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
+  log("EnsureFrontOfChain is not exported by the main executable.");
+  abort();
+}
diff --git a/sigchainlib/version-script.txt b/sigchainlib/version-script.txt
index 8030da4..ce15054 100644
--- a/sigchainlib/version-script.txt
+++ b/sigchainlib/version-script.txt
@@ -4,6 +4,7 @@
   UnclaimSignalChain;
   InvokeUserSignalHandler;
   InitializeSignalChain;
+  EnsureFrontOfChain;
   sigaction;
   signal;
   sigprocmask;
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 4ebbdc5..e3cf4ee 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -125,18 +125,28 @@
     }
 
     static class FinalizeCounter {
+      public static final int maxCount = 1024;
+      public static boolean finalized[] = new boolean[maxCount];
       private static Object finalizeLock = new Object();
       private static volatile int finalizeCount = 0;
       private int index;
       static int getCount() {
         return finalizeCount;
       }
+      static void printNonFinalized() {
+        for (int i = 0; i < maxCount; ++i) {
+          if (!FinalizeCounter.finalized[i]) {
+            System.err.println("Element " + i + " was not finalized");
+          }
+        }
+      }
       FinalizeCounter(int index) {
         this.index = index;
       }
       protected void finalize() {
         synchronized(finalizeLock) {
           ++finalizeCount;
+          finalized[index] = true;
         }
       }
     }
@@ -149,11 +159,21 @@
     }
 
     private static void runFinalizationTest() {
-      int count = 1024;
-      allocFinalizableObjects(count);
+      allocFinalizableObjects(FinalizeCounter.maxCount);
       Runtime.getRuntime().gc();
       System.runFinalization();
-      System.out.println("Finalized " + FinalizeCounter.getCount() + " / "  + count);
+      System.out.println("Finalized " + FinalizeCounter.getCount() + " / "  + FinalizeCounter.maxCount);
+      if (FinalizeCounter.getCount() != FinalizeCounter.maxCount) {
+        // Print out all the finalized elements.
+        FinalizeCounter.printNonFinalized();
+        // Try to sleep for a couple seconds to see if the objects became finalized after.
+        try {
+          java.lang.Thread.sleep(2000);
+        } catch (InterruptedException e) {
+        }
+        System.out.println("After sleep finalized " + FinalizeCounter.getCount() + " / "  + FinalizeCounter.maxCount);
+        FinalizeCounter.printNonFinalized();
+      }
     }
 
     public static class FinalizerTest {
diff --git a/test/074-gc-thrash/src/Main.java b/test/074-gc-thrash/src/Main.java
index 6ee7dce..32fbf2d 100644
--- a/test/074-gc-thrash/src/Main.java
+++ b/test/074-gc-thrash/src/Main.java
@@ -255,54 +255,55 @@
      * valid and invalid references on the stack.
      */
     private String dive(int depth, int iteration) {
-        String str0;
-        String str1;
-        String str2;
-        String str3;
-        String str4;
-        String str5;
-        String str6;
-        String str7;
-        String funStr;
+        try {
+            String str0;
+            String str1;
+            String str2;
+            String str3;
+            String str4;
+            String str5;
+            String str6;
+            String str7;
+            String funStr = "";
+            switch (iteration % 8) {
+                case 0:
+                    funStr = str0 = makeString(iteration);
+                    break;
+                case 1:
+                    funStr = str1 = makeString(iteration);
+                    break;
+                case 2:
+                    funStr = str2 = makeString(iteration);
+                    break;
+                case 3:
+                    funStr = str3 = makeString(iteration);
+                    break;
+                case 4:
+                    funStr = str4 = makeString(iteration);
+                    break;
+                case 5:
+                    funStr = str5 = makeString(iteration);
+                    break;
+                case 6:
+                    funStr = str6 = makeString(iteration);
+                    break;
+                case 7:
+                    funStr = str7 = makeString(iteration);
+                    break;
+            }
 
-        funStr = "";
-
-        switch (iteration % 8) {
-            case 0:
-                funStr = str0 = makeString(iteration);
-                break;
-            case 1:
-                funStr = str1 = makeString(iteration);
-                break;
-            case 2:
-                funStr = str2 = makeString(iteration);
-                break;
-            case 3:
-                funStr = str3 = makeString(iteration);
-                break;
-            case 4:
-                funStr = str4 = makeString(iteration);
-                break;
-            case 5:
-                funStr = str5 = makeString(iteration);
-                break;
-            case 6:
-                funStr = str6 = makeString(iteration);
-                break;
-            case 7:
-                funStr = str7 = makeString(iteration);
-                break;
+            strong[depth] = funStr;
+            weak[depth] = new WeakReference(funStr);
+            if (depth+1 < MAX_DEPTH)
+                dive(depth+1, iteration+1);
+            else
+                Main.sleep(100);
+            return funStr;
+        } catch (OutOfMemoryError e) {
+            // Silently ignore OOME since gc stress mode causes them to occur but shouldn't be a
+            // test failure.
         }
-
-        strong[depth] = funStr;
-        weak[depth] = new WeakReference(funStr);
-
-        if (depth+1 < MAX_DEPTH)
-            dive(depth+1, iteration+1);
-        else
-            Main.sleep(100);
-
-        return funStr;
+        return "";
     }
 
     private String makeString(int val) {