ART: Implement kMirOpNullCheck

The semantics of kMirOpNullCheck is to check object for null and
throw exception in that case. However, the implementation for it
is empty. This has been changed and appropriate dataflow have been
added to correctly reflect behavior.

In order to allow testing of implementation, the SpecialMethodInliner
has been updated to get rid of invoke and use this instead. This helps
all optimizations which do not check the MIR_INLINED flag because
when invoke is left in, they believe that invoke will still be done.

Change-Id: I62e425e42bdbc6357246fb949db5f79de73cf358
Signed-off-by: Razvan A Lupusoru <razvan.a.lupusoru@intel.com>
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 63f3e64..47cb424 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -114,7 +114,13 @@
   kMirOpFusedCmpgDouble,
   kMirOpFusedCmpLong,
   kMirOpNop,
+
+  // @brief Do a null check on the object register.
+  // @details The backends may implement this implicitly or explicitly. This MIR is guaranteed
+  // to have the correct offset as an exception thrower.
+  // vA: object register
   kMirOpNullCheck,
+
   kMirOpRangeCheck,
   kMirOpDivZeroCheck,
   kMirOpCheck,
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index b82c5c7..7d356a4 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -824,7 +824,7 @@
   DF_NOP,
 
   // 108 MIR_NULL_CHECK
-  0,
+  DF_UA | DF_REF_A | DF_NULL_CHK_0 | DF_LVN,
 
   // 109 MIR_RANGE_CHECK
   0,
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index dbceaff..5e0ba15 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -561,9 +561,25 @@
       break;
     default:
       LOG(FATAL) << "Unexpected inline op: " << method.opcode;
+      break;
   }
   if (result) {
     invoke->optimization_flags |= MIR_INLINED;
+    // If the invoke has not been eliminated yet, check now whether we should do it.
+    // This is done so that dataflow analysis does not get tripped up seeing nop invoke.
+    if (static_cast<int>(invoke->dalvikInsn.opcode) != kMirOpNop) {
+      bool is_static = invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC ||
+          invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE;
+      if (is_static || (invoke->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) {
+        // No null object register involved here so we can eliminate the invoke.
+        invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+      } else {
+        // Invoke was kept around because null check needed to be done.
+        invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNullCheck);
+        // For invokes, the object register is in vC. For null check mir, it is in vA.
+        invoke->dalvikInsn.vA = invoke->dalvikInsn.vC;
+      }
+    }
     if (move_result != nullptr) {
       move_result->optimization_flags |= MIR_INLINED;
       move_result->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index e519011..c4dfcb9 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -1077,9 +1077,17 @@
     case kMirOpSelect:
       GenSelect(bb, mir);
       break;
+    case kMirOpNullCheck: {
+      RegLocation rl_obj = mir_graph_->GetSrc(mir, 0);
+      rl_obj = LoadValue(rl_obj, kRefReg);
+      // An explicit check is done because it is not expected that when this is used,
+      // that it will actually trip up the implicit checks (since an invalid access
+      // is needed on the null object).
+      GenExplicitNullCheck(rl_obj.reg, mir->optimization_flags);
+      break;
+    }
     case kMirOpPhi:
     case kMirOpNop:
-    case kMirOpNullCheck:
     case kMirOpRangeCheck:
     case kMirOpDivZeroCheck:
     case kMirOpCheck: