Home | History | Annotate | Download | only in dex
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <vector>
     18 
     19 #include "base/logging.h"
     20 #include "dataflow_iterator.h"
     21 #include "dataflow_iterator-inl.h"
     22 #include "dex/compiler_ir.h"
     23 #include "dex/mir_field_info.h"
     24 #include "gtest/gtest.h"
     25 
     26 namespace art {
     27 
     28 class MirOptimizationTest : public testing::Test {
     29  protected:
     30   struct BBDef {
     31     static constexpr size_t kMaxSuccessors = 4;
     32     static constexpr size_t kMaxPredecessors = 4;
     33 
     34     BBType type;
     35     size_t num_successors;
     36     BasicBlockId successors[kMaxPredecessors];
     37     size_t num_predecessors;
     38     BasicBlockId predecessors[kMaxPredecessors];
     39   };
     40 
     41   struct MethodDef {
     42     uint16_t method_idx;
     43     uintptr_t declaring_dex_file;
     44     uint16_t declaring_class_idx;
     45     uint16_t declaring_method_idx;
     46     InvokeType invoke_type;
     47     InvokeType sharp_type;
     48     bool is_referrers_class;
     49     bool is_initialized;
     50   };
     51 
     52   struct MIRDef {
     53     BasicBlockId bbid;
     54     Instruction::Code opcode;
     55     uint32_t field_or_method_info;
     56     uint32_t vA;
     57     uint32_t vB;
     58     uint32_t vC;
     59   };
     60 
     61 #define DEF_SUCC0() \
     62     0u, { }
     63 #define DEF_SUCC1(s1) \
     64     1u, { s1 }
     65 #define DEF_SUCC2(s1, s2) \
     66     2u, { s1, s2 }
     67 #define DEF_SUCC3(s1, s2, s3) \
     68     3u, { s1, s2, s3 }
     69 #define DEF_SUCC4(s1, s2, s3, s4) \
     70     4u, { s1, s2, s3, s4 }
     71 #define DEF_PRED0() \
     72     0u, { }
     73 #define DEF_PRED1(p1) \
     74     1u, { p1 }
     75 #define DEF_PRED2(p1, p2) \
     76     2u, { p1, p2 }
     77 #define DEF_PRED3(p1, p2, p3) \
     78     3u, { p1, p2, p3 }
     79 #define DEF_PRED4(p1, p2, p3, p4) \
     80     4u, { p1, p2, p3, p4 }
     81 #define DEF_BB(type, succ, pred) \
     82     { type, succ, pred }
     83 
     84 #define DEF_SGET_SPUT(bb, opcode, vA, field_info) \
     85     { bb, opcode, field_info, vA, 0u, 0u }
     86 #define DEF_IGET_IPUT(bb, opcode, vA, vB, field_info) \
     87     { bb, opcode, field_info, vA, vB, 0u }
     88 #define DEF_AGET_APUT(bb, opcode, vA, vB, vC) \
     89     { bb, opcode, 0u, vA, vB, vC }
     90 #define DEF_INVOKE(bb, opcode, vC, method_info) \
     91     { bb, opcode, method_info, 0u, 0u, vC }
     92 #define DEF_OTHER0(bb, opcode) \
     93     { bb, opcode, 0u, 0u, 0u, 0u }
     94 #define DEF_OTHER1(bb, opcode, vA) \
     95     { bb, opcode, 0u, vA, 0u, 0u }
     96 #define DEF_OTHER2(bb, opcode, vA, vB) \
     97     { bb, opcode, 0u, vA, vB, 0u }
     98 
     99   void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
    100     cu_.mir_graph->block_id_map_.clear();
    101     cu_.mir_graph->block_list_.clear();
    102     ASSERT_LT(3u, count);  // null, entry, exit and at least one bytecode block.
    103     ASSERT_EQ(kNullBlock, defs[0].type);
    104     ASSERT_EQ(kEntryBlock, defs[1].type);
    105     ASSERT_EQ(kExitBlock, defs[2].type);
    106     for (size_t i = 0u; i != count; ++i) {
    107       const BBDef* def = &defs[i];
    108       BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type);
    109       if (def->num_successors <= 2) {
    110         bb->successor_block_list_type = kNotUsed;
    111         bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u;
    112         bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u;
    113       } else {
    114         bb->successor_block_list_type = kPackedSwitch;
    115         bb->fall_through = 0u;
    116         bb->taken = 0u;
    117         bb->successor_blocks.reserve(def->num_successors);
    118         for (size_t j = 0u; j != def->num_successors; ++j) {
    119           SuccessorBlockInfo* successor_block_info =
    120               static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
    121                                                                kArenaAllocSuccessor));
    122           successor_block_info->block = j;
    123           successor_block_info->key = 0u;  // Not used by class init check elimination.
    124           bb->successor_blocks.push_back(successor_block_info);
    125         }
    126       }
    127       bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors);
    128       if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) {
    129         bb->data_flow_info = static_cast<BasicBlockDataFlow*>(
    130             cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
    131       }
    132     }
    133     ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
    134     cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
    135     ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
    136     cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2];
    137     ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type);
    138   }
    139 
    140   template <size_t count>
    141   void PrepareBasicBlocks(const BBDef (&defs)[count]) {
    142     DoPrepareBasicBlocks(defs, count);
    143   }
    144 
    145   void PrepareSingleBlock() {
    146     static const BBDef bbs[] = {
    147         DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
    148         DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
    149         DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)),
    150         DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)),
    151     };
    152     PrepareBasicBlocks(bbs);
    153   }
    154 
    155   void PrepareDiamond() {
    156     static const BBDef bbs[] = {
    157         DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
    158         DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
    159         DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
    160         DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)),
    161         DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
    162         DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
    163         DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),
    164     };
    165     PrepareBasicBlocks(bbs);
    166   }
    167 
    168   void PrepareLoop() {
    169     static const BBDef bbs[] = {
    170         DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
    171         DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
    172         DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
    173         DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
    174         DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)),  // "taken" loops to self.
    175         DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
    176     };
    177     PrepareBasicBlocks(bbs);
    178   }
    179 
    180   void PrepareNestedLoopsWhile_While() {
    181     static const BBDef bbs[] = {
    182         DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
    183         DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
    184         DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(8)),
    185         DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
    186         DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED2(3, 7)),  // Outer while loop head.
    187         DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)),  // Inner while loop head.
    188         DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)),        // "taken" loops to inner head.
    189         DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(5)),        // "taken" loops to outer head.
    190         DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
    191     };
    192     PrepareBasicBlocks(bbs);
    193   }
    194 
    195   void PrepareNestedLoopsWhile_WhileWhile() {
    196     static const BBDef bbs[] = {
    197         DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
    198         DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
    199         DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)),
    200         DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
    201         DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 10), DEF_PRED2(3, 9)),   // Outer while loop head.
    202         DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)),    // Inner while loop head 1.
    203         DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)),          // Loops to inner head 1.
    204         DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(5, 8)),    // Inner while loop head 2.
    205         DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(7)),          // loops to inner head 2.
    206         DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(7)),          // loops to outer head.
    207         DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
    208     };
    209     PrepareBasicBlocks(bbs);
    210   }
    211 
    212   void PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge() {
    213     // Extra edge from the first inner loop body to second inner loop body (6u->8u).
    214     static const BBDef bbs[] = {
    215         DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
    216         DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
    217         DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)),
    218         DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
    219         DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 10), DEF_PRED2(3, 9)),   // Outer while loop head.
    220         DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)),    // Inner while loop head 1.
    221         DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED1(5)),       // Loops to inner head 1.
    222         DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(5, 8)),    // Inner while loop head 2.
    223         DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED2(7, 6)),       // loops to inner head 2.
    224         DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(7)),          // loops to outer head.
    225         DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
    226     };
    227     PrepareBasicBlocks(bbs);
    228   }
    229 
    230   void PrepareCatch() {
    231     static const BBDef bbs[] = {
    232         DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
    233         DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
    234         DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
    235         DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),     // The top.
    236         DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),     // The throwing insn.
    237         DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),     // Catch handler.
    238         DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),  // The merged block.
    239     };
    240     PrepareBasicBlocks(bbs);
    241     BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u);
    242     catch_handler->catch_entry = true;
    243     // Add successor block info to the check block.
    244     BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
    245     check_bb->successor_block_list_type = kCatch;
    246     SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
    247         (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
    248     successor_block_info->block = catch_handler->id;
    249     check_bb->successor_blocks.push_back(successor_block_info);
    250   }
    251 
    252   void DoPrepareMethods(const MethodDef* defs, size_t count) {
    253     cu_.mir_graph->method_lowering_infos_.clear();
    254     cu_.mir_graph->method_lowering_infos_.reserve(count);
    255     for (size_t i = 0u; i != count; ++i) {
    256       const MethodDef* def = &defs[i];
    257       MirMethodLoweringInfo method_info(def->method_idx, def->invoke_type, false);
    258       if (def->declaring_dex_file != 0u) {
    259         method_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
    260         method_info.declaring_class_idx_ = def->declaring_class_idx;
    261         method_info.declaring_method_idx_ = def->declaring_method_idx;
    262       }
    263       ASSERT_EQ(def->invoke_type != kStatic, def->sharp_type != kStatic);
    264       method_info.flags_ =
    265           ((def->invoke_type == kStatic) ? MirMethodLoweringInfo::kFlagIsStatic : 0u) |
    266           MirMethodLoweringInfo::kFlagFastPath |
    267           (static_cast<uint16_t>(def->invoke_type) << MirMethodLoweringInfo::kBitInvokeTypeBegin) |
    268           (static_cast<uint16_t>(def->sharp_type) << MirMethodLoweringInfo::kBitSharpTypeBegin) |
    269           ((def->is_referrers_class) ? MirMethodLoweringInfo::kFlagIsReferrersClass : 0u) |
    270           ((def->is_initialized == kStatic) ? MirMethodLoweringInfo::kFlagClassIsInitialized : 0u);
    271       ASSERT_EQ(def->declaring_dex_file != 0u, method_info.IsResolved());
    272       cu_.mir_graph->method_lowering_infos_.push_back(method_info);
    273     }
    274   }
    275 
    276   template <size_t count>
    277   void PrepareMethods(const MethodDef (&defs)[count]) {
    278     DoPrepareMethods(defs, count);
    279   }
    280 
    281   void DoPrepareMIRs(const MIRDef* defs, size_t count) {
    282     mir_count_ = count;
    283     mirs_ = cu_.arena.AllocArray<MIR>(count, kArenaAllocMIR);
    284     uint64_t merged_df_flags = 0u;
    285     for (size_t i = 0u; i != count; ++i) {
    286       const MIRDef* def = &defs[i];
    287       MIR* mir = &mirs_[i];
    288       mir->dalvikInsn.opcode = def->opcode;
    289       ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size());
    290       BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid];
    291       bb->AppendMIR(mir);
    292       if (IsInstructionIGetOrIPut(def->opcode)) {
    293         ASSERT_LT(def->field_or_method_info, cu_.mir_graph->ifield_lowering_infos_.size());
    294         mir->meta.ifield_lowering_info = def->field_or_method_info;
    295         ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_or_method_info].MemAccessType(),
    296                   IGetOrIPutMemAccessType(def->opcode));
    297       } else if (IsInstructionSGetOrSPut(def->opcode)) {
    298         ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size());
    299         mir->meta.sfield_lowering_info = def->field_or_method_info;
    300         ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_or_method_info].MemAccessType(),
    301                   SGetOrSPutMemAccessType(def->opcode));
    302       } else if (IsInstructionInvoke(def->opcode)) {
    303         ASSERT_LT(def->field_or_method_info, cu_.mir_graph->method_lowering_infos_.size());
    304         mir->meta.method_lowering_info = def->field_or_method_info;
    305       }
    306       mir->dalvikInsn.vA = def->vA;
    307       mir->dalvikInsn.vB = def->vB;
    308       mir->dalvikInsn.vC = def->vC;
    309       mir->ssa_rep = nullptr;
    310       mir->offset = 2 * i;  // All insns need to be at least 2 code units long.
    311       mir->optimization_flags = 0u;
    312       merged_df_flags |= MIRGraph::GetDataFlowAttributes(def->opcode);
    313     }
    314     cu_.mir_graph->merged_df_flags_ = merged_df_flags;
    315 
    316     code_item_ = static_cast<DexFile::CodeItem*>(
    317         cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc));
    318     memset(code_item_, 0, sizeof(DexFile::CodeItem));
    319     code_item_->insns_size_in_code_units_ = 2u * count;
    320     cu_.mir_graph->current_code_item_ = code_item_;
    321   }
    322 
    323   template <size_t count>
    324   void PrepareMIRs(const MIRDef (&defs)[count]) {
    325     DoPrepareMIRs(defs, count);
    326   }
    327 
    328   MirOptimizationTest()
    329       : pool_(),
    330         cu_(&pool_, kRuntimeISA, nullptr, nullptr),
    331         mir_count_(0u),
    332         mirs_(nullptr),
    333         code_item_(nullptr) {
    334     cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
    335     cu_.access_flags = kAccStatic;  // Don't let "this" interfere with this test.
    336   }
    337 
    338   ArenaPool pool_;
    339   CompilationUnit cu_;
    340   size_t mir_count_;
    341   MIR* mirs_;
    342   DexFile::CodeItem* code_item_;
    343 };
    344 
    345 class ClassInitCheckEliminationTest : public MirOptimizationTest {
    346  protected:
    347   struct SFieldDef {
    348     uint16_t field_idx;
    349     uintptr_t declaring_dex_file;
    350     uint16_t declaring_class_idx;
    351     uint16_t declaring_field_idx;
    352     DexMemAccessType type;
    353   };
    354 
    355   void DoPrepareSFields(const SFieldDef* defs, size_t count) {
    356     cu_.mir_graph->sfield_lowering_infos_.clear();
    357     cu_.mir_graph->sfield_lowering_infos_.reserve(count);
    358     for (size_t i = 0u; i != count; ++i) {
    359       const SFieldDef* def = &defs[i];
    360       MirSFieldLoweringInfo field_info(def->field_idx, def->type);
    361       if (def->declaring_dex_file != 0u) {
    362         field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
    363         field_info.declaring_class_idx_ = def->declaring_class_idx;
    364         field_info.declaring_field_idx_ = def->declaring_field_idx;
    365         // We don't care about the volatile flag in these tests.
    366       }
    367       ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
    368       ASSERT_FALSE(field_info.IsClassInitialized());
    369       cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
    370     }
    371   }
    372 
    373   template <size_t count>
    374   void PrepareSFields(const SFieldDef (&defs)[count]) {
    375     DoPrepareSFields(defs, count);
    376   }
    377 
    378   void PerformClassInitCheckElimination() {
    379     cu_.mir_graph->ComputeDFSOrders();
    380     bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate();
    381     ASSERT_TRUE(gate_result);
    382     RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get());
    383     bool change = false;
    384     for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
    385       change = cu_.mir_graph->EliminateClassInitChecks(bb);
    386     }
    387     cu_.mir_graph->EliminateClassInitChecksEnd();
    388   }
    389 
    390   ClassInitCheckEliminationTest()
    391       : MirOptimizationTest() {
    392   }
    393 };
    394 
    395 class NullCheckEliminationTest : public MirOptimizationTest {
    396  protected:
    397   struct IFieldDef {
    398     uint16_t field_idx;
    399     uintptr_t declaring_dex_file;
    400     uint16_t declaring_class_idx;
    401     uint16_t declaring_field_idx;
    402     DexMemAccessType type;
    403   };
    404 
    405   void DoPrepareIFields(const IFieldDef* defs, size_t count) {
    406     cu_.mir_graph->ifield_lowering_infos_.clear();
    407     cu_.mir_graph->ifield_lowering_infos_.reserve(count);
    408     for (size_t i = 0u; i != count; ++i) {
    409       const IFieldDef* def = &defs[i];
    410       MirIFieldLoweringInfo field_info(def->field_idx, def->type, false);
    411       if (def->declaring_dex_file != 0u) {
    412         field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
    413         field_info.declaring_class_idx_ = def->declaring_class_idx;
    414         field_info.declaring_field_idx_ = def->declaring_field_idx;
    415         // We don't care about the volatile flag in these tests.
    416       }
    417       ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
    418       cu_.mir_graph->ifield_lowering_infos_.push_back(field_info);
    419     }
    420   }
    421 
    422   template <size_t count>
    423   void PrepareIFields(const IFieldDef (&defs)[count]) {
    424     DoPrepareIFields(defs, count);
    425   }
    426 
    427   void PerformNullCheckElimination() {
    428     // Make vregs in range [100, 1000) input registers, i.e. requiring a null check.
    429     code_item_->registers_size_ = 1000;
    430     code_item_->ins_size_ = 900;
    431 
    432     cu_.mir_graph->ComputeDFSOrders();
    433     bool gate_result = cu_.mir_graph->EliminateNullChecksGate();
    434     ASSERT_TRUE(gate_result);
    435     RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get());
    436     bool change = false;
    437     for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
    438       change = cu_.mir_graph->EliminateNullChecks(bb);
    439     }
    440     cu_.mir_graph->EliminateNullChecksEnd();
    441   }
    442 
    443   NullCheckEliminationTest()
    444       : MirOptimizationTest() {
    445     static const MethodDef methods[] = {
    446         { 0u, 1u, 0u, 0u, kDirect, kDirect, false, false },  // Dummy.
    447     };
    448     PrepareMethods(methods);
    449   }
    450 };
    451 
    452 class SuspendCheckEliminationTest : public MirOptimizationTest {
    453  protected:
    454   bool IsBackEdge(BasicBlockId branch_bb, BasicBlockId target_bb) {
    455     BasicBlock* branch = cu_.mir_graph->GetBasicBlock(branch_bb);
    456     return target_bb != NullBasicBlockId && cu_.mir_graph->IsBackEdge(branch, target_bb);
    457   }
    458 
    459   bool IsSuspendCheckEdge(BasicBlockId branch_bb, BasicBlockId target_bb) {
    460     BasicBlock* branch = cu_.mir_graph->GetBasicBlock(branch_bb);
    461     return cu_.mir_graph->IsSuspendCheckEdge(branch, target_bb);
    462   }
    463 
    464   void PerformSuspendCheckElimination() {
    465     cu_.mir_graph->SSATransformationStart();
    466     cu_.mir_graph->ComputeDFSOrders();
    467     cu_.mir_graph->ComputeDominators();
    468     cu_.mir_graph->ComputeTopologicalSortOrder();
    469     cu_.mir_graph->SSATransformationEnd();
    470 
    471     bool gate_result = cu_.mir_graph->EliminateSuspendChecksGate();
    472     ASSERT_NE(gate_result, kLeafOptimization);
    473     if (kLeafOptimization) {
    474       // Even with kLeafOptimization on and Gate() refusing to allow SCE, we want
    475       // to run the SCE test to avoid bitrot, so we need to initialize explicitly.
    476       cu_.mir_graph->suspend_checks_in_loops_ =
    477           cu_.mir_graph->arena_->AllocArray<uint32_t>(cu_.mir_graph->GetNumBlocks(),
    478                                                       kArenaAllocMisc);
    479     }
    480 
    481     TopologicalSortIterator iterator(cu_.mir_graph.get());
    482     bool change = false;
    483     for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
    484       change = cu_.mir_graph->EliminateSuspendChecks(bb);
    485     }
    486   }
    487 
    488   SuspendCheckEliminationTest()
    489       : MirOptimizationTest() {
    490     static const MethodDef methods[] = {
    491         { 0u, 1u, 0u, 0u, kDirect, kDirect, false, false },  // Dummy.
    492     };
    493     PrepareMethods(methods);
    494   }
    495 };
    496 
    497 TEST_F(ClassInitCheckEliminationTest, SingleBlock) {
    498   static const SFieldDef sfields[] = {
    499       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    500       { 1u, 1u, 1u, 1u, kDexMemAccessWord },
    501       { 2u, 1u, 2u, 2u, kDexMemAccessWord },
    502       { 3u, 1u, 3u, 3u, kDexMemAccessWord },  // Same declaring class as sfield[4].
    503       { 4u, 1u, 3u, 4u, kDexMemAccessWord },  // Same declaring class as sfield[3].
    504       { 5u, 0u, 0u, 0u, kDexMemAccessWord },  // Unresolved.
    505   };
    506   static const MIRDef mirs[] = {
    507       DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 5u),  // Unresolved.
    508       DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u),
    509       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
    510       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u),
    511       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 5u),  // Unresolved.
    512       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),
    513       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
    514       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u),
    515       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 5u),  // Unresolved.
    516       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 3u),
    517       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 4u),
    518   };
    519   static const bool expected_ignore_clinit_check[] = {
    520       false, false, false, false, true, true, true, true, true, false, true
    521   };
    522 
    523   PrepareSFields(sfields);
    524   PrepareSingleBlock();
    525   PrepareMIRs(mirs);
    526   PerformClassInitCheckElimination();
    527   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
    528   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    529     EXPECT_EQ(expected_ignore_clinit_check[i],
    530               (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
    531     EXPECT_EQ(expected_ignore_clinit_check[i],
    532               (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
    533   }
    534 }
    535 
    536 TEST_F(ClassInitCheckEliminationTest, SingleBlockWithInvokes) {
    537   static const SFieldDef sfields[] = {
    538       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    539       { 1u, 1u, 1u, 1u, kDexMemAccessWord },
    540       { 2u, 1u, 2u, 2u, kDexMemAccessWord },
    541   };
    542   static const MethodDef methods[] = {
    543       { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false },
    544       { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false },
    545       { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false },
    546   };
    547   static const MIRDef mirs[] = {
    548       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),
    549       DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
    550       DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
    551       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
    552       DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
    553       DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
    554   };
    555   static const bool expected_class_initialized[] = {
    556       false, true, false, true, false, true
    557   };
    558   static const bool expected_class_in_dex_cache[] = {
    559       false, false, false, false, false, false
    560   };
    561 
    562   PrepareSFields(sfields);
    563   PrepareMethods(methods);
    564   PrepareSingleBlock();
    565   PrepareMIRs(mirs);
    566   PerformClassInitCheckElimination();
    567   ASSERT_EQ(arraysize(expected_class_initialized), mir_count_);
    568   ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_);
    569   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    570     EXPECT_EQ(expected_class_initialized[i],
    571               (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
    572     EXPECT_EQ(expected_class_in_dex_cache[i],
    573               (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
    574   }
    575 }
    576 
    577 TEST_F(ClassInitCheckEliminationTest, Diamond) {
    578   static const SFieldDef sfields[] = {
    579       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    580       { 1u, 1u, 1u, 1u, kDexMemAccessWord },
    581       { 2u, 1u, 2u, 2u, kDexMemAccessWord },
    582       { 3u, 1u, 3u, 3u, kDexMemAccessWord },
    583       { 4u, 1u, 4u, 4u, kDexMemAccessWord },
    584       { 5u, 1u, 5u, 5u, kDexMemAccessWord },
    585       { 6u, 1u, 6u, 6u, kDexMemAccessWord },
    586       { 7u, 1u, 7u, 7u, kDexMemAccessWord },
    587       { 8u, 1u, 8u, 8u, kDexMemAccessWord },   // Same declaring class as sfield[9].
    588       { 9u, 1u, 8u, 9u, kDexMemAccessWord },   // Same declaring class as sfield[8].
    589       { 10u, 0u, 0u, 0u, kDexMemAccessWord },  // Unresolved.
    590   };
    591   static const MIRDef mirs[] = {
    592       // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
    593       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 10u),  // Unresolved.
    594       DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 10u),  // Unresolved.
    595       DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u),
    596       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 0u),  // Eliminated (BB #3 dominates #6).
    597       DEF_SGET_SPUT(4u, Instruction::SPUT, 0u, 1u),
    598       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 1u),  // Not eliminated (BB #4 doesn't dominate #6).
    599       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u),
    600       DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),  // Eliminated (BB #3 dominates #4).
    601       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 3u),
    602       DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 3u),  // Eliminated (BB #3 dominates #5).
    603       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 4u),
    604       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 4u),  // Eliminated (BB #3 dominates #6).
    605       DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 5u),
    606       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 5u),  // Not eliminated (BB #4 doesn't dominate #6).
    607       DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 6u),
    608       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 6u),  // Not eliminated (BB #5 doesn't dominate #6).
    609       DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 7u),
    610       DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 7u),
    611       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 7u),  // Eliminated (initialized in both #3 and #4).
    612       DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 8u),
    613       DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 9u),
    614       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 8u),  // Eliminated (with sfield[9] in BB #5).
    615       DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 9u),  // Eliminated (with sfield[8] in BB #4).
    616   };
    617   static const bool expected_ignore_clinit_check[] = {
    618       false, true,          // Unresolved: sfield[10]
    619       false, true,          // sfield[0]
    620       false, false,         // sfield[1]
    621       false, true,          // sfield[2]
    622       false, true,          // sfield[3]
    623       false, true,          // sfield[4]
    624       false, false,         // sfield[5]
    625       false, false,         // sfield[6]
    626       false, false, true,   // sfield[7]
    627       false, false, true, true,  // sfield[8], sfield[9]
    628   };
    629 
    630   PrepareSFields(sfields);
    631   PrepareDiamond();
    632   PrepareMIRs(mirs);
    633   PerformClassInitCheckElimination();
    634   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
    635   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    636     EXPECT_EQ(expected_ignore_clinit_check[i],
    637               (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
    638     EXPECT_EQ(expected_ignore_clinit_check[i],
    639               (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
    640   }
    641 }
    642 
    643 TEST_F(ClassInitCheckEliminationTest, DiamondWithInvokes) {
    644   static const SFieldDef sfields[] = {
    645       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    646       { 1u, 1u, 1u, 1u, kDexMemAccessWord },
    647       { 2u, 1u, 2u, 2u, kDexMemAccessWord },
    648       { 3u, 1u, 3u, 3u, kDexMemAccessWord },
    649       { 4u, 1u, 4u, 4u, kDexMemAccessWord },
    650   };
    651   static const MethodDef methods[] = {
    652       { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false },
    653       { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false },
    654       { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false },
    655       { 3u, 1u, 3u, 3u, kStatic, kStatic, false, false },
    656       { 4u, 1u, 4u, 4u, kStatic, kStatic, false, false },
    657   };
    658   static const MIRDef mirs[] = {
    659       // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
    660       DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u),
    661       DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
    662       DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
    663       DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 1u),
    664       DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),
    665       DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
    666       DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 2u),
    667       DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 3u),
    668       DEF_SGET_SPUT(5u, Instruction::SPUT, 0u, 3u),
    669       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 3u),
    670       DEF_SGET_SPUT(4u, Instruction::SPUT, 0u, 4u),
    671       DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 4u),
    672       DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u /* dummy */, 4u),
    673   };
    674   static const bool expected_class_initialized[] = {
    675       false, true,    // BB #3 SPUT, BB#6 INVOKE_STATIC
    676       false, true,    // BB #3 INVOKE_STATIC, BB#6 SPUT
    677       false, false, true,   // BB #4 SGET, BB #5 INVOKE_STATIC, BB #6 SPUT
    678       false, false, true,   // BB #4 INVOKE_STATIC, BB #5 SPUT, BB #6 SGET
    679       false, false, true,   // BB #4 SPUT, BB #5 SGET, BB #6 INVOKE_STATIC
    680   };
    681   static const bool expected_class_in_dex_cache[] = {
    682       false, false,   // BB #3 SPUT, BB#6 INVOKE_STATIC
    683       false, false,   // BB #3 INVOKE_STATIC, BB#6 SPUT
    684       false, false, false,  // BB #4 SGET, BB #5 INVOKE_STATIC, BB #6 SPUT
    685       false, false, false,  // BB #4 INVOKE_STATIC, BB #5 SPUT, BB #6 SGET
    686       false, false, false,  // BB #4 SPUT, BB #5 SGET, BB #6 INVOKE_STATIC
    687   };
    688 
    689   PrepareSFields(sfields);
    690   PrepareMethods(methods);
    691   PrepareDiamond();
    692   PrepareMIRs(mirs);
    693   PerformClassInitCheckElimination();
    694   ASSERT_EQ(arraysize(expected_class_initialized), mir_count_);
    695   ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_);
    696   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    697     EXPECT_EQ(expected_class_initialized[i],
    698               (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
    699     EXPECT_EQ(expected_class_in_dex_cache[i],
    700               (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
    701   }
    702 }
    703 
    704 TEST_F(ClassInitCheckEliminationTest, Loop) {
    705   static const SFieldDef sfields[] = {
    706       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    707       { 1u, 1u, 1u, 1u, kDexMemAccessWord },
    708       { 2u, 1u, 2u, 2u, kDexMemAccessWord },
    709   };
    710   static const MIRDef mirs[] = {
    711       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),
    712       DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 0u),  // Eliminated.
    713       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
    714       DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 1u),  // Eliminated.
    715       DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),
    716       DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 2u),  // Eliminated.
    717   };
    718   static const bool expected_ignore_clinit_check[] = {
    719       false, true, false, true, false, true,
    720   };
    721 
    722   PrepareSFields(sfields);
    723   PrepareLoop();
    724   PrepareMIRs(mirs);
    725   PerformClassInitCheckElimination();
    726   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
    727   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    728     EXPECT_EQ(expected_ignore_clinit_check[i],
    729               (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
    730     EXPECT_EQ(expected_ignore_clinit_check[i],
    731               (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
    732   }
    733 }
    734 
    735 TEST_F(ClassInitCheckEliminationTest, LoopWithInvokes) {
    736   static const SFieldDef sfields[] = {
    737       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    738   };
    739   static const MethodDef methods[] = {
    740       { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false },
    741       { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false },
    742       { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false },
    743   };
    744   static const MIRDef mirs[] = {
    745       DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
    746       DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
    747       DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
    748       DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
    749       DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
    750       DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
    751       DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 0u),
    752   };
    753   static const bool expected_class_initialized[] = {
    754       false, true, false, true, false, true, true,
    755   };
    756   static const bool expected_class_in_dex_cache[] = {
    757       false, false, false, false, false, false, false,
    758   };
    759 
    760   PrepareSFields(sfields);
    761   PrepareMethods(methods);
    762   PrepareLoop();
    763   PrepareMIRs(mirs);
    764   PerformClassInitCheckElimination();
    765   ASSERT_EQ(arraysize(expected_class_initialized), mir_count_);
    766   ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_);
    767   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    768     EXPECT_EQ(expected_class_initialized[i],
    769               (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
    770     EXPECT_EQ(expected_class_in_dex_cache[i],
    771               (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
    772   }
    773 }
    774 
    775 TEST_F(ClassInitCheckEliminationTest, Catch) {
    776   static const SFieldDef sfields[] = {
    777       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    778       { 1u, 1u, 1u, 1u, kDexMemAccessWord },
    779       { 2u, 1u, 2u, 2u, kDexMemAccessWord },
    780       { 3u, 1u, 3u, 3u, kDexMemAccessWord },
    781   };
    782   static const MIRDef mirs[] = {
    783       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),  // Before the exception edge.
    784       DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),  // Before the exception edge.
    785       DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),  // After the exception edge.
    786       DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 3u),  // After the exception edge.
    787       DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 0u),  // In catch handler; eliminated.
    788       DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 2u),  // In catch handler; not eliminated.
    789       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 0u),  // Class init check eliminated.
    790       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 1u),  // Class init check eliminated.
    791       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 2u),  // Class init check eliminated.
    792       DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 3u),  // Class init check not eliminated.
    793   };
    794   static const bool expected_ignore_clinit_check[] = {
    795       false, false, false, false, true, false, true, true, true, false
    796   };
    797 
    798   PrepareSFields(sfields);
    799   PrepareCatch();
    800   PrepareMIRs(mirs);
    801   PerformClassInitCheckElimination();
    802   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
    803   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    804     EXPECT_EQ(expected_ignore_clinit_check[i],
    805               (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
    806     EXPECT_EQ(expected_ignore_clinit_check[i],
    807               (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
    808   }
    809 }
    810 
    811 TEST_F(NullCheckEliminationTest, SingleBlock) {
    812   static const IFieldDef ifields[] = {
    813       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    814       { 1u, 1u, 0u, 1u, kDexMemAccessWord },
    815       { 2u, 1u, 0u, 2u, kDexMemAccessObject },
    816   };
    817   static const MIRDef mirs[] = {
    818       DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 0u, 100u, 2u),
    819       DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 0u, 1u),
    820       DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 2u, 100u, 2u),  // Differs from 0u (no LVN here).
    821       DEF_IGET_IPUT(3u, Instruction::IGET, 3u, 2u, 1u),
    822       DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 101u, 0u),
    823       DEF_IGET_IPUT(3u, Instruction::IGET, 5u, 102u, 0u),
    824       DEF_IGET_IPUT(3u, Instruction::IGET, 6u, 103u, 0u),
    825       DEF_IGET_IPUT(3u, Instruction::IGET, 7u, 103u, 1u),
    826       DEF_IGET_IPUT(3u, Instruction::IPUT, 8u, 104u, 0u),
    827       DEF_IGET_IPUT(3u, Instruction::IPUT, 9u, 104u, 1u),
    828       DEF_IGET_IPUT(3u, Instruction::IGET, 10u, 105u, 0u),
    829       DEF_IGET_IPUT(3u, Instruction::IPUT, 11u, 105u, 1u),
    830       DEF_IGET_IPUT(3u, Instruction::IPUT, 12u, 106u, 0u),
    831       DEF_IGET_IPUT(3u, Instruction::IGET, 13u, 106u, 1u),
    832       DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 107, 0u /* dummy */),
    833       DEF_IGET_IPUT(3u, Instruction::IGET, 15u, 107u, 1u),
    834       DEF_IGET_IPUT(3u, Instruction::IGET, 16u, 108u, 0u),
    835       DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 108, 0u /* dummy */),
    836       DEF_AGET_APUT(3u, Instruction::AGET, 18u, 109u, 110u),
    837       DEF_AGET_APUT(3u, Instruction::APUT, 19u, 109u, 111u),
    838       DEF_OTHER2(3u, Instruction::ARRAY_LENGTH, 20u, 112u),
    839       DEF_AGET_APUT(3u, Instruction::AGET, 21u, 112u, 113u),
    840       DEF_OTHER1(3u, Instruction::MONITOR_ENTER, 114u),
    841       DEF_OTHER1(3u, Instruction::MONITOR_EXIT, 114u),
    842   };
    843   static const bool expected_ignore_null_check[] = {
    844       false, false, true, false /* Not doing LVN. */,
    845       false, true /* Set before running NCE. */,
    846       false, true,  // IGET, IGET
    847       false, true,  // IPUT, IPUT
    848       false, true,  // IGET, IPUT
    849       false, true,  // IPUT, IGET
    850       false, true,  // INVOKE, IGET
    851       false, true,  // IGET, INVOKE
    852       false, true,  // AGET, APUT
    853       false, true,  // ARRAY_LENGTH, AGET
    854       false, true,  // MONITOR_ENTER, MONITOR_EXIT
    855   };
    856 
    857   PrepareIFields(ifields);
    858   PrepareSingleBlock();
    859   PrepareMIRs(mirs);
    860 
    861   // Mark IGET 5u as null-checked to test that NCE doesn't clear this flag.
    862   mirs_[5u].optimization_flags |= MIR_IGNORE_NULL_CHECK;
    863 
    864   PerformNullCheckElimination();
    865   ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
    866   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    867     EXPECT_EQ(expected_ignore_null_check[i],
    868               (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
    869   }
    870 }
    871 
    872 TEST_F(NullCheckEliminationTest, Diamond) {
    873   static const IFieldDef ifields[] = {
    874       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    875       { 1u, 1u, 0u, 1u, kDexMemAccessWord },
    876       { 2u, 1u, 0u, 2u, kDexMemAccessObject },  // int[].
    877   };
    878   static const MIRDef mirs[] = {
    879       // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
    880       DEF_IGET_IPUT(3u, Instruction::IPUT, 0u, 100u, 0u),
    881       DEF_IGET_IPUT(6u, Instruction::IGET, 1u, 100u, 1u),  // Eliminated (BB #3 dominates #6).
    882       DEF_IGET_IPUT(3u, Instruction::IGET, 2u, 101u, 0u),
    883       DEF_IGET_IPUT(4u, Instruction::IPUT, 3u, 101u, 0u),  // Eliminated (BB #3 dominates #4).
    884       DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u),
    885       DEF_IGET_IPUT(5u, Instruction::IPUT, 5u, 102u, 1u),  // Eliminated (BB #3 dominates #5).
    886       DEF_IGET_IPUT(4u, Instruction::IPUT, 6u, 103u, 0u),
    887       DEF_IGET_IPUT(6u, Instruction::IPUT, 7u, 103u, 1u),  // Not eliminated (going through BB #5).
    888       DEF_IGET_IPUT(5u, Instruction::IGET, 8u, 104u, 1u),
    889       DEF_IGET_IPUT(6u, Instruction::IGET, 9u, 104u, 0u),  // Not eliminated (going through BB #4).
    890       DEF_INVOKE(4u, Instruction::INVOKE_DIRECT, 105u, 0u /* dummy */),
    891       DEF_IGET_IPUT(5u, Instruction::IGET, 11u, 105u, 1u),
    892       DEF_IGET_IPUT(6u, Instruction::IPUT, 12u, 105u, 0u),  // Eliminated.
    893       DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 13u, 106u, 2u),
    894       DEF_OTHER1(3u, Instruction::IF_EQZ, 13u),            // Last insn in the BB #3.
    895       DEF_OTHER2(5u, Instruction::NEW_ARRAY, 13u, 107u),
    896       DEF_AGET_APUT(6u, Instruction::AGET, 16u, 13u, 108u),  // Eliminated.
    897   };
    898   static const bool expected_ignore_null_check[] = {
    899       false, true,   // BB #3 IPUT, BB #6 IGET
    900       false, true,   // BB #3 IGET, BB #4 IPUT
    901       false, true,   // BB #3 IGET, BB #5 IPUT
    902       false, false,  // BB #4 IPUT, BB #6 IPUT
    903       false, false,  // BB #5 IGET, BB #6 IGET
    904       false, false, true,  // BB #4 INVOKE, BB #5 IGET, BB #6 IPUT
    905       false, false,  // BB #3 IGET_OBJECT & IF_EQZ
    906       false, true,   // BB #5 NEW_ARRAY, BB #6 AGET
    907   };
    908 
    909   PrepareIFields(ifields);
    910   PrepareDiamond();
    911   PrepareMIRs(mirs);
    912   PerformNullCheckElimination();
    913   ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
    914   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    915     EXPECT_EQ(expected_ignore_null_check[i],
    916               (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
    917   }
    918 }
    919 
    920 TEST_F(NullCheckEliminationTest, Loop) {
    921   static const IFieldDef ifields[] = {
    922       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    923       { 1u, 1u, 1u, 1u, kDexMemAccessWord },
    924   };
    925   static const MIRDef mirs[] = {
    926       DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u),
    927       DEF_IGET_IPUT(4u, Instruction::IGET, 1u, 101u, 0u),
    928       DEF_IGET_IPUT(5u, Instruction::IGET, 2u, 100u, 1u),  // Eliminated.
    929       DEF_IGET_IPUT(5u, Instruction::IGET, 3u, 101u, 1u),  // Eliminated.
    930       DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u),
    931       DEF_IGET_IPUT(4u, Instruction::IGET, 5u, 102u, 1u),  // Not eliminated (MOVE_OBJECT_16).
    932       DEF_OTHER2(4u, Instruction::MOVE_OBJECT_16, 102u, 103u),
    933   };
    934   static const bool expected_ignore_null_check[] = {
    935       false, false, true, true,
    936       false, false, false,
    937   };
    938 
    939   PrepareIFields(ifields);
    940   PrepareLoop();
    941   PrepareMIRs(mirs);
    942   PerformNullCheckElimination();
    943   ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
    944   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    945     EXPECT_EQ(expected_ignore_null_check[i],
    946               (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
    947   }
    948 }
    949 
    950 TEST_F(NullCheckEliminationTest, Catch) {
    951   static const IFieldDef ifields[] = {
    952       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
    953       { 1u, 1u, 1u, 1u, kDexMemAccessWord },
    954   };
    955   static const MIRDef mirs[] = {
    956       DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u),  // Before the exception edge.
    957       DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 101u, 0u),  // Before the exception edge.
    958       DEF_IGET_IPUT(4u, Instruction::IGET, 2u, 102u, 0u),  // After the exception edge.
    959       DEF_IGET_IPUT(4u, Instruction::IGET, 3u, 103u, 0u),  // After the exception edge.
    960       DEF_IGET_IPUT(5u, Instruction::IGET, 4u, 100u, 1u),  // In catch handler; eliminated.
    961       DEF_IGET_IPUT(5u, Instruction::IGET, 5u, 102u, 1u),  // In catch handler; not eliminated.
    962       DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 100u, 0u),  // Null check eliminated.
    963       DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 101u, 1u),  // Null check eliminated.
    964       DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 102u, 0u),  // Null check eliminated.
    965       DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 103u, 1u),  // Null check not eliminated.
    966   };
    967   static const bool expected_ignore_null_check[] = {
    968       false, false, false, false, true, false, true, true, true, false
    969   };
    970 
    971   PrepareIFields(ifields);
    972   PrepareCatch();
    973   PrepareMIRs(mirs);
    974   PerformNullCheckElimination();
    975   ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
    976   for (size_t i = 0u; i != arraysize(mirs); ++i) {
    977     EXPECT_EQ(expected_ignore_null_check[i],
    978               (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
    979   }
    980 }
    981 
    982 TEST_F(SuspendCheckEliminationTest, LoopNoElimination) {
    983   static const MIRDef mirs[] = {
    984     DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u, 0u),  // Force the pass to run.
    985     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge back.
    986   };
    987 
    988   PrepareLoop();
    989   PrepareMIRs(mirs);
    990   PerformSuspendCheckElimination();
    991   ASSERT_TRUE(IsBackEdge(4u, 4u));
    992   EXPECT_TRUE(IsSuspendCheckEdge(4u, 4u));  // Suspend point on loop to self.
    993 }
    994 
    995 TEST_F(SuspendCheckEliminationTest, LoopElimination) {
    996   static const MIRDef mirs[] = {
    997     DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in the loop.
    998     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge back.
    999   };
   1000 
   1001   PrepareLoop();
   1002   PrepareMIRs(mirs);
   1003   PerformSuspendCheckElimination();
   1004   ASSERT_TRUE(IsBackEdge(4u, 4u));
   1005   EXPECT_FALSE(IsSuspendCheckEdge(4u, 4u));  // No suspend point on loop to self.
   1006 }
   1007 
   1008 TEST_F(SuspendCheckEliminationTest, While_While_NoElimination) {
   1009   static const MIRDef mirs[] = {
   1010     DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u, 0u),  // Force the pass to run.
   1011     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
   1012     DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop.
   1013     DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
   1014     DEF_OTHER0(7u, Instruction::GOTO),                   // Edge back to outer loop head.
   1015   };
   1016 
   1017   PrepareNestedLoopsWhile_While();
   1018   PrepareMIRs(mirs);
   1019   PerformSuspendCheckElimination();
   1020   ASSERT_TRUE(IsBackEdge(6u, 5u));
   1021   EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
   1022   ASSERT_TRUE(IsBackEdge(7u, 4u));
   1023   EXPECT_TRUE(IsSuspendCheckEdge(7u, 4u));
   1024 }
   1025 
   1026 TEST_F(SuspendCheckEliminationTest, While_While_InvokeInOuterLoopHead) {
   1027   static const MIRDef mirs[] = {
   1028     DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in outer loop head.
   1029     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
   1030     DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop.
   1031     DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
   1032     DEF_OTHER0(7u, Instruction::GOTO),                   // Edge back to outer loop head.
   1033   };
   1034 
   1035   PrepareNestedLoopsWhile_While();
   1036   PrepareMIRs(mirs);
   1037   PerformSuspendCheckElimination();
   1038   ASSERT_TRUE(IsBackEdge(6u, 5u));
   1039   EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
   1040   ASSERT_TRUE(IsBackEdge(7u, 4u));
   1041   EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u));
   1042 }
   1043 
   1044 TEST_F(SuspendCheckEliminationTest, While_While_InvokeInOuterLoopBody) {
   1045   static const MIRDef mirs[] = {
   1046     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
   1047     DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop.
   1048     DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
   1049     DEF_INVOKE(7u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in outer loop body.
   1050     DEF_OTHER0(7u, Instruction::GOTO),                   // Edge back to outer loop head.
   1051   };
   1052 
   1053   PrepareNestedLoopsWhile_While();
   1054   PrepareMIRs(mirs);
   1055   PerformSuspendCheckElimination();
   1056   ASSERT_TRUE(IsBackEdge(6u, 5u));
   1057   EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
   1058   ASSERT_TRUE(IsBackEdge(7u, 4u));
   1059   EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u));
   1060 }
   1061 
   1062 TEST_F(SuspendCheckEliminationTest, While_While_InvokeInInnerLoopHead) {
   1063   static const MIRDef mirs[] = {
   1064     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
   1065     DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in inner loop head.
   1066     DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop.
   1067     DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
   1068     DEF_OTHER0(7u, Instruction::GOTO),                   // Edge back to outer loop head.
   1069   };
   1070 
   1071   PrepareNestedLoopsWhile_While();
   1072   PrepareMIRs(mirs);
   1073   PerformSuspendCheckElimination();
   1074   ASSERT_TRUE(IsBackEdge(6u, 5u));
   1075   EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
   1076   ASSERT_TRUE(IsBackEdge(7u, 4u));
   1077   EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u));
   1078 }
   1079 
   1080 TEST_F(SuspendCheckEliminationTest, While_While_InvokeInInnerLoopBody) {
   1081   static const MIRDef mirs[] = {
   1082     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
   1083     DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop.
   1084     DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in inner loop body.
   1085     DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
   1086     DEF_OTHER0(7u, Instruction::GOTO),                   // Edge back to outer loop head.
   1087   };
   1088 
   1089   PrepareNestedLoopsWhile_While();
   1090   PrepareMIRs(mirs);
   1091   PerformSuspendCheckElimination();
   1092   ASSERT_TRUE(IsBackEdge(6u, 5u));
   1093   EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
   1094   ASSERT_TRUE(IsBackEdge(7u, 4u));
   1095   EXPECT_TRUE(IsSuspendCheckEdge(7u, 4u));
   1096 }
   1097 
   1098 TEST_F(SuspendCheckEliminationTest, While_WhileWhile_InvokeInFirstInnerLoopHead) {
   1099   static const MIRDef mirs[] = {
   1100     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
   1101     DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in first inner loop head.
   1102     DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 1.
   1103     DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
   1104     DEF_OTHER1(7u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 2.
   1105     DEF_OTHER0(8u, Instruction::GOTO),                   // Edge back to inner loop 2 head.
   1106     DEF_OTHER0(9u, Instruction::GOTO),                   // Edge back to outer loop head.
   1107   };
   1108 
   1109   PrepareNestedLoopsWhile_WhileWhile();
   1110   PrepareMIRs(mirs);
   1111   PerformSuspendCheckElimination();
   1112   ASSERT_TRUE(IsBackEdge(6u, 5u));
   1113   EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
   1114   ASSERT_TRUE(IsBackEdge(8u, 7u));
   1115   EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u));
   1116   ASSERT_TRUE(IsBackEdge(9u, 4u));
   1117   EXPECT_FALSE(IsSuspendCheckEdge(9u, 4u));
   1118 }
   1119 
   1120 TEST_F(SuspendCheckEliminationTest, While_WhileWhile_InvokeInFirstInnerLoopBody) {
   1121   static const MIRDef mirs[] = {
   1122     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
   1123     DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 1.
   1124     DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in first inner loop body.
   1125     DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
   1126     DEF_OTHER1(7u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 2.
   1127     DEF_OTHER0(8u, Instruction::GOTO),                   // Edge back to inner loop 2 head.
   1128     DEF_OTHER0(9u, Instruction::GOTO),                   // Edge back to outer loop head.
   1129   };
   1130 
   1131   PrepareNestedLoopsWhile_WhileWhile();
   1132   PrepareMIRs(mirs);
   1133   PerformSuspendCheckElimination();
   1134   ASSERT_TRUE(IsBackEdge(6u, 5u));
   1135   EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
   1136   ASSERT_TRUE(IsBackEdge(8u, 7u));
   1137   EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u));
   1138   ASSERT_TRUE(IsBackEdge(9u, 4u));
   1139   EXPECT_TRUE(IsSuspendCheckEdge(9u, 4u));
   1140 }
   1141 
   1142 TEST_F(SuspendCheckEliminationTest, While_WhileWhile_WithExtraEdge_InvokeInFirstInnerLoopBody) {
   1143   static const MIRDef mirs[] = {
   1144     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
   1145     DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 1.
   1146     DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in first inner loop body.
   1147     DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
   1148     DEF_OTHER1(7u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 2.
   1149     DEF_OTHER0(8u, Instruction::GOTO),                   // Edge back to inner loop 2 head.
   1150     DEF_OTHER0(9u, Instruction::GOTO),                   // Edge back to outer loop head.
   1151   };
   1152 
   1153   PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge();
   1154   PrepareMIRs(mirs);
   1155   PerformSuspendCheckElimination();
   1156   ASSERT_TRUE(IsBackEdge(6u, 5u));
   1157   EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
   1158   ASSERT_TRUE(IsBackEdge(8u, 7u));
   1159   EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u));  // Unaffected by the extra edge.
   1160   ASSERT_TRUE(IsBackEdge(9u, 4u));
   1161   EXPECT_TRUE(IsSuspendCheckEdge(9u, 4u));
   1162 }
   1163 
   1164 TEST_F(SuspendCheckEliminationTest, While_WhileWhile_WithExtraEdge_InvokeInSecondInnerLoopHead) {
   1165   static const MIRDef mirs[] = {
   1166     DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
   1167     DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 1.
   1168     DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
   1169     DEF_INVOKE(7u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in second inner loop head.
   1170     DEF_OTHER1(7u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 2.
   1171     DEF_OTHER0(8u, Instruction::GOTO),                   // Edge back to inner loop 2 head.
   1172     DEF_OTHER0(9u, Instruction::GOTO),                   // Edge back to outer loop head.
   1173   };
   1174 
   1175   PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge();
   1176   PrepareMIRs(mirs);
   1177   PerformSuspendCheckElimination();
   1178   ASSERT_TRUE(IsBackEdge(6u, 5u));
   1179   EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
   1180   ASSERT_TRUE(IsBackEdge(8u, 7u));
   1181   EXPECT_FALSE(IsSuspendCheckEdge(8u, 7u));  // Unaffected by the extra edge.
   1182   ASSERT_TRUE(IsBackEdge(9u, 4u));
   1183   EXPECT_FALSE(IsSuspendCheckEdge(9u, 4u));
   1184 }
   1185 
   1186 }  // namespace art
   1187