Rewrite class initialization check elimination.

Split the notion of type being in dex cache away from the
class being initialized. Include static invokes in the class
initialization elimination pass.

Change-Id: Ie3760d8fd55b987f9507f32ef51456a57d79e3fb
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index 337d4ef..8874faf 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -36,10 +36,21 @@
     BasicBlockId predecessors[kMaxPredecessors];
   };
 
+  struct MethodDef {
+    uint16_t method_idx;
+    uintptr_t declaring_dex_file;
+    uint16_t declaring_class_idx;
+    uint16_t declaring_method_idx;
+    InvokeType invoke_type;
+    InvokeType sharp_type;
+    bool is_referrers_class;
+    bool is_initialized;
+  };
+
   struct MIRDef {
     BasicBlockId bbid;
     Instruction::Code opcode;
-    uint32_t field_info;
+    uint32_t field_or_method_info;
     uint32_t vA;
     uint32_t vB;
     uint32_t vC;
@@ -68,6 +79,19 @@
 #define DEF_BB(type, succ, pred) \
     { type, succ, pred }
 
+#define DEF_SGET_SPUT(bb, opcode, vA, field_info) \
+    { bb, opcode, field_info, vA, 0u, 0u }
+#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, method_info) \
+    { bb, opcode, method_info, 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 }
+
   void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
     cu_.mir_graph->block_id_map_.clear();
     cu_.mir_graph->block_list_.clear();
@@ -172,6 +196,35 @@
     check_bb->successor_blocks.push_back(successor_block_info);
   }
 
+  void DoPrepareMethods(const MethodDef* defs, size_t count) {
+    cu_.mir_graph->method_lowering_infos_.clear();
+    cu_.mir_graph->method_lowering_infos_.reserve(count);
+    for (size_t i = 0u; i != count; ++i) {
+      const MethodDef* def = &defs[i];
+      MirMethodLoweringInfo method_info(def->method_idx, def->invoke_type);
+      if (def->declaring_dex_file != 0u) {
+        method_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
+        method_info.declaring_class_idx_ = def->declaring_class_idx;
+        method_info.declaring_method_idx_ = def->declaring_method_idx;
+      }
+      ASSERT_EQ(def->invoke_type != kStatic, def->sharp_type != kStatic);
+      method_info.flags_ =
+          ((def->invoke_type == kStatic) ? MirMethodLoweringInfo::kFlagIsStatic : 0u) |
+          MirMethodLoweringInfo::kFlagFastPath |
+          (static_cast<uint16_t>(def->invoke_type) << MirMethodLoweringInfo::kBitInvokeTypeBegin) |
+          (static_cast<uint16_t>(def->sharp_type) << MirMethodLoweringInfo::kBitSharpTypeBegin) |
+          ((def->is_referrers_class) ? MirMethodLoweringInfo::kFlagIsReferrersClass : 0u) |
+          ((def->is_initialized == kStatic) ? MirMethodLoweringInfo::kFlagClassIsInitialized : 0u);
+      ASSERT_EQ(def->declaring_dex_file != 0u, method_info.IsResolved());
+      cu_.mir_graph->method_lowering_infos_.push_back(method_info);
+    }
+  }
+
+  template <size_t count>
+  void PrepareMethods(const MethodDef (&defs)[count]) {
+    DoPrepareMethods(defs, count);
+  }
+
   void DoPrepareMIRs(const MIRDef* defs, size_t count) {
     mir_count_ = count;
     mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, kArenaAllocMIR));
@@ -184,11 +237,16 @@
       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_info, cu_.mir_graph->sfield_lowering_infos_.size());
-        mir->meta.sfield_lowering_info = def->field_info;
+        ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size());
+        mir->meta.sfield_lowering_info = def->field_or_method_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;
+        ASSERT_LT(def->field_or_method_info, cu_.mir_graph->ifield_lowering_infos_.size());
+        mir->meta.ifield_lowering_info = def->field_or_method_info;
+      } else if (def->opcode >= Instruction::INVOKE_VIRTUAL &&
+          def->opcode < Instruction::INVOKE_INTERFACE_RANGE &&
+          def->opcode != Instruction::RETURN_VOID_BARRIER) {
+        ASSERT_LT(def->field_or_method_info, cu_.mir_graph->method_lowering_infos_.size());
+        mir->meta.method_lowering_info = def->field_or_method_info;
       }
       mir->dalvikInsn.vA = def->vA;
       mir->dalvikInsn.vB = def->vB;
@@ -251,7 +309,7 @@
         field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic;
       }
       ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
-      ASSERT_FALSE(field_info.IsInitialized());
+      ASSERT_FALSE(field_info.IsClassInitialized());
       cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
     }
   }
@@ -326,12 +384,13 @@
 
   NullCheckEliminationTest()
       : MirOptimizationTest() {
+    static const MethodDef methods[] = {
+        { 0u, 1u, 0u, 0u, kDirect, kDirect, false, false },  // Dummy.
+    };
+    PrepareMethods(methods);
   }
 };
 
-#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 },
@@ -342,17 +401,17 @@
       { 5u, 0u, 0u, 0u },  // Unresolved.
   };
   static const MIRDef mirs[] = {
-      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),
+      DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 5u),  // Unresolved.
+      DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 5u),  // Unresolved.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 5u),  // Unresolved.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 3u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 4u),
   };
   static const bool expected_ignore_clinit_check[] = {
       false, false, false, false, true, true, true, true, true, false, true
@@ -365,7 +424,50 @@
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
   for (size_t i = 0u; i != arraysize(mirs); ++i) {
     EXPECT_EQ(expected_ignore_clinit_check[i],
-              (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_ignore_clinit_check[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
+  }
+}
+
+TEST_F(ClassInitCheckEliminationTest, SingleBlockWithInvokes) {
+  static const SFieldDef sfields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 1u, 1u },
+      { 2u, 1u, 2u, 2u },
+  };
+  static const MethodDef methods[] = {
+      { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false },
+      { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false },
+      { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false },
+  };
+  static const MIRDef mirs[] = {
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
+  };
+  static const bool expected_class_initialized[] = {
+      false, true, false, true, false, true
+  };
+  static const bool expected_class_in_dex_cache[] = {
+      false, false, false, false, false, false
+  };
+
+  PrepareSFields(sfields);
+  PrepareMethods(methods);
+  PrepareSingleBlock();
+  PrepareMIRs(mirs);
+  PerformClassInitCheckElimination();
+  ASSERT_EQ(arraysize(expected_class_initialized), mir_count_);
+  ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_class_initialized[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_class_in_dex_cache[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
   }
 }
 
@@ -385,32 +487,32 @@
   };
   static const MIRDef mirs[] = {
       // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
-      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).
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 10u),  // Unresolved.
+      DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 10u),  // Unresolved.
+      DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 0u),  // Eliminated (BB #3 dominates #6).
+      DEF_SGET_SPUT(4u, Instruction::SPUT, 0u, 1u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 1u),  // Not eliminated (BB #4 doesn't dominate #6).
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u),
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),  // Eliminated (BB #3 dominates #4).
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 3u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 3u),  // Eliminated (BB #3 dominates #5).
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 4u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 4u),  // Eliminated (BB #3 dominates #6).
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 5u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 5u),  // Not eliminated (BB #4 doesn't dominate #6).
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 6u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 6u),  // Not eliminated (BB #5 doesn't dominate #6).
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 7u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 7u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 7u),  // Eliminated (initialized in both #3 and #4).
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 8u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 9u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 8u),  // Eliminated (with sfield[9] in BB #5).
+      DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 9u),  // Eliminated (with sfield[8] in BB #4).
   };
   static const bool expected_ignore_clinit_check[] = {
-      false, true,          // Unresolved: sfield[10], method[2]
+      false, true,          // Unresolved: sfield[10]
       false, true,          // sfield[0]
       false, false,         // sfield[1]
       false, true,          // sfield[2]
@@ -429,7 +531,70 @@
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
   for (size_t i = 0u; i != arraysize(mirs); ++i) {
     EXPECT_EQ(expected_ignore_clinit_check[i],
-              (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_ignore_clinit_check[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
+  }
+}
+
+TEST_F(ClassInitCheckEliminationTest, DiamondWithInvokes) {
+  static const SFieldDef sfields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 1u, 1u },
+      { 2u, 1u, 2u, 2u },
+      { 3u, 1u, 3u, 3u },
+      { 4u, 1u, 4u, 4u },
+  };
+  static const MethodDef methods[] = {
+      { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false },
+      { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false },
+      { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false },
+      { 3u, 1u, 3u, 3u, kStatic, kStatic, false, false },
+      { 4u, 1u, 4u, 4u, kStatic, kStatic, false, false },
+  };
+  static const MIRDef mirs[] = {
+      // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
+      DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u),
+      DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
+      DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 1u),
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),
+      DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
+      DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 2u),
+      DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 3u),
+      DEF_SGET_SPUT(5u, Instruction::SPUT, 0u, 3u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 3u),
+      DEF_SGET_SPUT(4u, Instruction::SPUT, 0u, 4u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 4u),
+      DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u /* dummy */, 4u),
+  };
+  static const bool expected_class_initialized[] = {
+      false, true,    // BB #3 SPUT, BB#6 INVOKE_STATIC
+      false, true,    // BB #3 INVOKE_STATIC, BB#6 SPUT
+      false, false, true,   // BB #4 SGET, BB #5 INVOKE_STATIC, BB #6 SPUT
+      false, false, true,   // BB #4 INVOKE_STATIC, BB #5 SPUT, BB #6 SGET
+      false, false, true,   // BB #4 SPUT, BB #5 SGET, BB #6 INVOKE_STATIC
+  };
+  static const bool expected_class_in_dex_cache[] = {
+      false, false,   // BB #3 SPUT, BB#6 INVOKE_STATIC
+      false, false,   // BB #3 INVOKE_STATIC, BB#6 SPUT
+      false, false, false,  // BB #4 SGET, BB #5 INVOKE_STATIC, BB #6 SPUT
+      false, false, false,  // BB #4 INVOKE_STATIC, BB #5 SPUT, BB #6 SGET
+      false, false, false,  // BB #4 SPUT, BB #5 SGET, BB #6 INVOKE_STATIC
+  };
+
+  PrepareSFields(sfields);
+  PrepareMethods(methods);
+  PrepareDiamond();
+  PrepareMIRs(mirs);
+  PerformClassInitCheckElimination();
+  ASSERT_EQ(arraysize(expected_class_initialized), mir_count_);
+  ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_class_initialized[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_class_in_dex_cache[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
   }
 }
 
@@ -437,15 +602,18 @@
   static const SFieldDef sfields[] = {
       { 0u, 1u, 0u, 0u },
       { 1u, 1u, 1u, 1u },
+      { 2u, 1u, 2u, 2u },
   };
   static const MIRDef mirs[] = {
-      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.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 0u),  // Eliminated.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 1u),  // Eliminated.
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 2u),  // Eliminated.
   };
   static const bool expected_ignore_clinit_check[] = {
-      false, false, true, true
+      false, true, false, true, false, true,
   };
 
   PrepareSFields(sfields);
@@ -455,7 +623,49 @@
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
   for (size_t i = 0u; i != arraysize(mirs); ++i) {
     EXPECT_EQ(expected_ignore_clinit_check[i],
-              (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_ignore_clinit_check[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
+  }
+}
+
+TEST_F(ClassInitCheckEliminationTest, LoopWithInvokes) {
+  static const SFieldDef sfields[] = {
+      { 0u, 1u, 0u, 0u },
+  };
+  static const MethodDef methods[] = {
+      { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false },
+      { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false },
+      { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false },
+  };
+  static const MIRDef mirs[] = {
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
+      DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
+      DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
+      DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
+      DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 0u),
+  };
+  static const bool expected_class_initialized[] = {
+      false, true, false, true, false, true, true,
+  };
+  static const bool expected_class_in_dex_cache[] = {
+      false, false, false, false, false, false, false,
+  };
+
+  PrepareSFields(sfields);
+  PrepareMethods(methods);
+  PrepareLoop();
+  PrepareMIRs(mirs);
+  PerformClassInitCheckElimination();
+  ASSERT_EQ(arraysize(expected_class_initialized), mir_count_);
+  ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_class_initialized[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_class_in_dex_cache[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
   }
 }
 
@@ -467,16 +677,16 @@
       { 3u, 1u, 3u, 3u },
   };
   static const MIRDef mirs[] = {
-      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.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),  // Before the exception edge.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),  // Before the exception edge.
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),  // After the exception edge.
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 3u),  // After the exception edge.
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 0u),  // In catch handler; eliminated.
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 2u),  // In catch handler; not eliminated.
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 0u),  // Class init check eliminated.
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 1u),  // Class init check eliminated.
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 2u),  // Class init check eliminated.
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 3u),  // Class init check not eliminated.
   };
   static const bool expected_ignore_clinit_check[] = {
       false, false, false, false, true, false, true, true, true, false
@@ -489,21 +699,12 @@
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
   for (size_t i = 0u; i != arraysize(mirs); ++i) {
     EXPECT_EQ(expected_ignore_clinit_check[i],
-              (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_ignore_clinit_check[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
   }
 }
 
-#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 },
@@ -525,10 +726,10 @@
       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_INVOKE(3u, Instruction::INVOKE_DIRECT, 107, 0u /* dummy */),
       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_INVOKE(3u, Instruction::INVOKE_DIRECT, 108, 0u /* dummy */),
       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),
@@ -583,7 +784,7 @@
       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_INVOKE(4u, Instruction::INVOKE_DIRECT, 105u, 0u /* dummy */),
       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),