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 
DoPrepareBasicBlocks(const BBDef * defs,size_t count)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>
PrepareBasicBlocks(const BBDef (& defs)[count])141   void PrepareBasicBlocks(const BBDef (&defs)[count]) {
142     DoPrepareBasicBlocks(defs, count);
143   }
144 
PrepareSingleBlock()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 
PrepareDiamond()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 
PrepareLoop()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 
PrepareNestedLoopsWhile_While()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 
PrepareNestedLoopsWhile_WhileWhile()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 
PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge()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 
PrepareCatch()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 
DoPrepareMethods(const MethodDef * defs,size_t count)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>
PrepareMethods(const MethodDef (& defs)[count])277   void PrepareMethods(const MethodDef (&defs)[count]) {
278     DoPrepareMethods(defs, count);
279   }
280 
DoPrepareMIRs(const MIRDef * defs,size_t count)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>
PrepareMIRs(const MIRDef (& defs)[count])324   void PrepareMIRs(const MIRDef (&defs)[count]) {
325     DoPrepareMIRs(defs, count);
326   }
327 
MirOptimizationTest()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 
DoPrepareSFields(const SFieldDef * defs,size_t count)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>
PrepareSFields(const SFieldDef (& defs)[count])374   void PrepareSFields(const SFieldDef (&defs)[count]) {
375     DoPrepareSFields(defs, count);
376   }
377 
PerformClassInitCheckElimination()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 
ClassInitCheckEliminationTest()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 
DoPrepareIFields(const IFieldDef * defs,size_t count)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>
PrepareIFields(const IFieldDef (& defs)[count])423   void PrepareIFields(const IFieldDef (&defs)[count]) {
424     DoPrepareIFields(defs, count);
425   }
426 
PerformNullCheckElimination()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 
NullCheckEliminationTest()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:
IsBackEdge(BasicBlockId branch_bb,BasicBlockId target_bb)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 
IsSuspendCheckEdge(BasicBlockId branch_bb,BasicBlockId target_bb)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 
PerformSuspendCheckElimination()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 
SuspendCheckEliminationTest()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 
TEST_F(ClassInitCheckEliminationTest,SingleBlock)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 
TEST_F(ClassInitCheckEliminationTest,SingleBlockWithInvokes)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 
TEST_F(ClassInitCheckEliminationTest,Diamond)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 
TEST_F(ClassInitCheckEliminationTest,DiamondWithInvokes)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 
TEST_F(ClassInitCheckEliminationTest,Loop)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 
TEST_F(ClassInitCheckEliminationTest,LoopWithInvokes)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 
TEST_F(ClassInitCheckEliminationTest,Catch)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 
TEST_F(NullCheckEliminationTest,SingleBlock)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 
TEST_F(NullCheckEliminationTest,Diamond)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 
TEST_F(NullCheckEliminationTest,Loop)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 
TEST_F(NullCheckEliminationTest,Catch)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 
TEST_F(SuspendCheckEliminationTest,LoopNoElimination)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 
TEST_F(SuspendCheckEliminationTest,LoopElimination)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 
TEST_F(SuspendCheckEliminationTest,While_While_NoElimination)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 
TEST_F(SuspendCheckEliminationTest,While_While_InvokeInOuterLoopHead)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 
TEST_F(SuspendCheckEliminationTest,While_While_InvokeInOuterLoopBody)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 
TEST_F(SuspendCheckEliminationTest,While_While_InvokeInInnerLoopHead)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 
TEST_F(SuspendCheckEliminationTest,While_While_InvokeInInnerLoopBody)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 
TEST_F(SuspendCheckEliminationTest,While_WhileWhile_InvokeInFirstInnerLoopHead)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 
TEST_F(SuspendCheckEliminationTest,While_WhileWhile_InvokeInFirstInnerLoopBody)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 
TEST_F(SuspendCheckEliminationTest,While_WhileWhile_WithExtraEdge_InvokeInFirstInnerLoopBody)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 
TEST_F(SuspendCheckEliminationTest,While_WhileWhile_WithExtraEdge_InvokeInSecondInnerLoopHead)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