ART: NullCheckElimination should converge with MIR_IGNORE_NULL_CHECK

If the MIRGraph::EliminateNullChecksAndInferTypes() function managed
to prove that some regs are non-null then it sets the flag
MIR_IGNORE_NULL_CHECK and resets this flag for all the other regs.
If some previous optimizations have already set MIR_IGNORE_NULL_CHECK
then it can be reset by EliminateNullChecksAndInferTypes. This way
NullCheckElimination discards some optimization efforts.
Optimization passes should not reset MIR_IGNORE_NULL_CHECK unless
they 100% sure NullCheck is needed.

This patch makes the NCE_TypeInference pass merge its own
calculated MIR_IGNORE_NULL_CHECK with the one came from previous
optimizations. Technically NCE_TypeInference calculates the flag
in a temporary MIR_MARK-th bit by preserving MIR_IGNORE_NULL_CHECK.
Then at the end of NCE pass MIR_MARK is or-ed with
MIR_IGNORE_NULL_CHECK.

Change-Id: Ib26997c70ecf2c158f61496dee9b1fe45c812096
Signed-off-by: Yevgeny Rouban <yevgeny.y.rouban@intel.com>
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/mir_graph.h b/compiler/dex/mir_graph.h
index cc215bd..90cdded 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -160,6 +160,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
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 84c056d..01f55b9 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -833,6 +833,15 @@
   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;
 }
 
@@ -926,12 +935,10 @@
       int src_sreg = mir->ssa_rep->uses[src_idx];
       if (!ssa_regs_to_check->IsBitSet(src_sreg)) {
         // 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;
+        mir->optimization_flags &= ~MIR_MARK;
         // Mark s_reg as null-checked
         ssa_regs_to_check->ClearBit(src_sreg);
       }
@@ -1036,6 +1043,18 @@
   temp_bit_matrix_ = nullptr;
   DCHECK(temp_scoped_alloc_.get() != nullptr);
   temp_scoped_alloc_.reset();
+
+  // converge MIR_MARK with MIR_IGNORE_NULL_CHECK
+  const int MARK_TO_IGNORE_NULL_CHECK_SHIFT = kMIRMark - kMIRIgnoreNullCheck;
+  DCHECK(MARK_TO_IGNORE_NULL_CHECK_SHIFT > 0);
+  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) {
+      uint16_t mirMarkAdjustedToIgnoreNullCheck =
+          (mir->optimization_flags & MIR_MARK) >> MARK_TO_IGNORE_NULL_CHECK_SHIFT;
+      mir->optimization_flags |= mirMarkAdjustedToIgnoreNullCheck;
+    }
+  }
 }
 
 /*