blob: 337d4efda34cf82cb8fa4da50bdd1c7b11a10406 [file] [log] [blame]
Vladimir Markobfea9c22014-01-17 17:49:33 +00001/*
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 "compiler_internals.h"
20#include "dataflow_iterator.h"
21#include "dataflow_iterator-inl.h"
22#include "gtest/gtest.h"
23
24namespace art {
25
Vladimir Marko7baa6f82014-10-09 18:01:24 +010026class MirOptimizationTest : public testing::Test {
Vladimir Markobfea9c22014-01-17 17:49:33 +000027 protected:
Vladimir Markobfea9c22014-01-17 17:49:33 +000028 struct BBDef {
29 static constexpr size_t kMaxSuccessors = 4;
30 static constexpr size_t kMaxPredecessors = 4;
31
32 BBType type;
33 size_t num_successors;
34 BasicBlockId successors[kMaxPredecessors];
35 size_t num_predecessors;
36 BasicBlockId predecessors[kMaxPredecessors];
37 };
38
39 struct MIRDef {
Vladimir Markobfea9c22014-01-17 17:49:33 +000040 BasicBlockId bbid;
Vladimir Marko7baa6f82014-10-09 18:01:24 +010041 Instruction::Code opcode;
42 uint32_t field_info;
43 uint32_t vA;
44 uint32_t vB;
45 uint32_t vC;
Vladimir Markobfea9c22014-01-17 17:49:33 +000046 };
47
48#define DEF_SUCC0() \
49 0u, { }
50#define DEF_SUCC1(s1) \
51 1u, { s1 }
52#define DEF_SUCC2(s1, s2) \
53 2u, { s1, s2 }
54#define DEF_SUCC3(s1, s2, s3) \
55 3u, { s1, s2, s3 }
56#define DEF_SUCC4(s1, s2, s3, s4) \
57 4u, { s1, s2, s3, s4 }
58#define DEF_PRED0() \
59 0u, { }
60#define DEF_PRED1(p1) \
61 1u, { p1 }
62#define DEF_PRED2(p1, p2) \
63 2u, { p1, p2 }
64#define DEF_PRED3(p1, p2, p3) \
65 3u, { p1, p2, p3 }
66#define DEF_PRED4(p1, p2, p3, p4) \
67 4u, { p1, p2, p3, p4 }
68#define DEF_BB(type, succ, pred) \
69 { type, succ, pred }
70
Vladimir Markobfea9c22014-01-17 17:49:33 +000071 void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
72 cu_.mir_graph->block_id_map_.clear();
Vladimir Markoe39c54e2014-09-22 14:50:02 +010073 cu_.mir_graph->block_list_.clear();
Vladimir Markobfea9c22014-01-17 17:49:33 +000074 ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block.
75 ASSERT_EQ(kNullBlock, defs[0].type);
76 ASSERT_EQ(kEntryBlock, defs[1].type);
77 ASSERT_EQ(kExitBlock, defs[2].type);
78 for (size_t i = 0u; i != count; ++i) {
79 const BBDef* def = &defs[i];
Vladimir Markoe39c54e2014-09-22 14:50:02 +010080 BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type);
Vladimir Markobfea9c22014-01-17 17:49:33 +000081 if (def->num_successors <= 2) {
82 bb->successor_block_list_type = kNotUsed;
Vladimir Markobfea9c22014-01-17 17:49:33 +000083 bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u;
84 bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u;
85 } else {
86 bb->successor_block_list_type = kPackedSwitch;
87 bb->fall_through = 0u;
88 bb->taken = 0u;
Vladimir Markoe39c54e2014-09-22 14:50:02 +010089 bb->successor_blocks.reserve(def->num_successors);
Vladimir Markobfea9c22014-01-17 17:49:33 +000090 for (size_t j = 0u; j != def->num_successors; ++j) {
91 SuccessorBlockInfo* successor_block_info =
92 static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
93 kArenaAllocSuccessor));
94 successor_block_info->block = j;
95 successor_block_info->key = 0u; // Not used by class init check elimination.
Vladimir Markoe39c54e2014-09-22 14:50:02 +010096 bb->successor_blocks.push_back(successor_block_info);
Vladimir Markobfea9c22014-01-17 17:49:33 +000097 }
98 }
Vladimir Markoe39c54e2014-09-22 14:50:02 +010099 bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors);
Vladimir Markobfea9c22014-01-17 17:49:33 +0000100 if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) {
101 bb->data_flow_info = static_cast<BasicBlockDataFlow*>(
102 cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
103 }
104 }
105 cu_.mir_graph->num_blocks_ = count;
Vladimir Markoe39c54e2014-09-22 14:50:02 +0100106 ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
107 cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
Vladimir Markobfea9c22014-01-17 17:49:33 +0000108 ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
Vladimir Markoe39c54e2014-09-22 14:50:02 +0100109 cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2];
Vladimir Markobfea9c22014-01-17 17:49:33 +0000110 ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type);
111 }
112
113 template <size_t count>
114 void PrepareBasicBlocks(const BBDef (&defs)[count]) {
115 DoPrepareBasicBlocks(defs, count);
116 }
117
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100118 void PrepareSingleBlock() {
119 static const BBDef bbs[] = {
120 DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
121 DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
122 DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)),
123 DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)),
124 };
125 PrepareBasicBlocks(bbs);
126 }
127
128 void PrepareDiamond() {
129 static const BBDef bbs[] = {
130 DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
131 DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
132 DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
133 DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)),
134 DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
135 DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
136 DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),
137 };
138 PrepareBasicBlocks(bbs);
139 }
140
141 void PrepareLoop() {
142 static const BBDef bbs[] = {
143 DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
144 DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
145 DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
146 DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
147 DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self.
148 DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
149 };
150 PrepareBasicBlocks(bbs);
151 }
152
153 void PrepareCatch() {
154 static const BBDef bbs[] = {
155 DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
156 DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
157 DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
158 DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), // The top.
159 DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // The throwing insn.
160 DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Catch handler.
161 DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // The merged block.
162 };
163 PrepareBasicBlocks(bbs);
164 BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u);
165 catch_handler->catch_entry = true;
166 // Add successor block info to the check block.
167 BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
168 check_bb->successor_block_list_type = kCatch;
169 SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
170 (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
171 successor_block_info->block = catch_handler->id;
172 check_bb->successor_blocks.push_back(successor_block_info);
173 }
174
Vladimir Markobfea9c22014-01-17 17:49:33 +0000175 void DoPrepareMIRs(const MIRDef* defs, size_t count) {
176 mir_count_ = count;
177 mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, kArenaAllocMIR));
178 uint64_t merged_df_flags = 0u;
179 for (size_t i = 0u; i != count; ++i) {
180 const MIRDef* def = &defs[i];
181 MIR* mir = &mirs_[i];
182 mir->dalvikInsn.opcode = def->opcode;
Vladimir Markoe39c54e2014-09-22 14:50:02 +0100183 ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size());
184 BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid];
Jean Christophe Beylercdacac42014-03-13 14:54:59 -0700185 bb->AppendMIR(mir);
Vladimir Markobfea9c22014-01-17 17:49:33 +0000186 if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) {
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100187 ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size());
188 mir->meta.sfield_lowering_info = def->field_info;
189 } else if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) {
190 ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size());
191 mir->meta.ifield_lowering_info = def->field_info;
Vladimir Markobfea9c22014-01-17 17:49:33 +0000192 }
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100193 mir->dalvikInsn.vA = def->vA;
194 mir->dalvikInsn.vB = def->vB;
195 mir->dalvikInsn.vC = def->vC;
Vladimir Markobfea9c22014-01-17 17:49:33 +0000196 mir->ssa_rep = nullptr;
197 mir->offset = 2 * i; // All insns need to be at least 2 code units long.
Vladimir Markobfea9c22014-01-17 17:49:33 +0000198 mir->optimization_flags = 0u;
Jean Christophe Beylercc794c32014-05-02 09:34:13 -0700199 merged_df_flags |= MIRGraph::GetDataFlowAttributes(def->opcode);
Vladimir Markobfea9c22014-01-17 17:49:33 +0000200 }
201 cu_.mir_graph->merged_df_flags_ = merged_df_flags;
202
203 code_item_ = static_cast<DexFile::CodeItem*>(
204 cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc));
205 memset(code_item_, 0, sizeof(DexFile::CodeItem));
206 code_item_->insns_size_in_code_units_ = 2u * count;
Razvan A Lupusoru75035972014-09-11 15:24:59 -0700207 cu_.mir_graph->current_code_item_ = code_item_;
Vladimir Markobfea9c22014-01-17 17:49:33 +0000208 }
209
210 template <size_t count>
211 void PrepareMIRs(const MIRDef (&defs)[count]) {
212 DoPrepareMIRs(defs, count);
213 }
214
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100215 MirOptimizationTest()
Vladimir Markobfea9c22014-01-17 17:49:33 +0000216 : pool_(),
217 cu_(&pool_),
218 mir_count_(0u),
219 mirs_(nullptr),
220 code_item_(nullptr) {
221 cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100222 cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test.
Vladimir Markobfea9c22014-01-17 17:49:33 +0000223 }
224
225 ArenaPool pool_;
226 CompilationUnit cu_;
227 size_t mir_count_;
228 MIR* mirs_;
229 DexFile::CodeItem* code_item_;
230};
231
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100232class ClassInitCheckEliminationTest : public MirOptimizationTest {
233 protected:
234 struct SFieldDef {
235 uint16_t field_idx;
236 uintptr_t declaring_dex_file;
237 uint16_t declaring_class_idx;
238 uint16_t declaring_field_idx;
239 };
240
241 void DoPrepareSFields(const SFieldDef* defs, size_t count) {
242 cu_.mir_graph->sfield_lowering_infos_.clear();
243 cu_.mir_graph->sfield_lowering_infos_.reserve(count);
244 for (size_t i = 0u; i != count; ++i) {
245 const SFieldDef* def = &defs[i];
246 MirSFieldLoweringInfo field_info(def->field_idx);
247 if (def->declaring_dex_file != 0u) {
248 field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
249 field_info.declaring_class_idx_ = def->declaring_class_idx;
250 field_info.declaring_field_idx_ = def->declaring_field_idx;
251 field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic;
252 }
253 ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
254 ASSERT_FALSE(field_info.IsInitialized());
255 cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
256 }
257 }
258
259 template <size_t count>
260 void PrepareSFields(const SFieldDef (&defs)[count]) {
261 DoPrepareSFields(defs, count);
262 }
263
264 void PerformClassInitCheckElimination() {
265 cu_.mir_graph->ComputeDFSOrders();
266 bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate();
267 ASSERT_TRUE(gate_result);
268 RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get());
269 bool change = false;
270 for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
271 change = cu_.mir_graph->EliminateClassInitChecks(bb);
272 }
273 cu_.mir_graph->EliminateClassInitChecksEnd();
274 }
275
276 ClassInitCheckEliminationTest()
277 : MirOptimizationTest() {
278 }
279};
280
281class NullCheckEliminationTest : public MirOptimizationTest {
282 protected:
283 struct IFieldDef {
284 uint16_t field_idx;
285 uintptr_t declaring_dex_file;
286 uint16_t declaring_class_idx;
287 uint16_t declaring_field_idx;
288 };
289
290 void DoPrepareIFields(const IFieldDef* defs, size_t count) {
291 cu_.mir_graph->ifield_lowering_infos_.clear();
292 cu_.mir_graph->ifield_lowering_infos_.reserve(count);
293 for (size_t i = 0u; i != count; ++i) {
294 const IFieldDef* def = &defs[i];
295 MirIFieldLoweringInfo field_info(def->field_idx);
296 if (def->declaring_dex_file != 0u) {
297 field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
298 field_info.declaring_class_idx_ = def->declaring_class_idx;
299 field_info.declaring_field_idx_ = def->declaring_field_idx;
300 }
301 ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
302 cu_.mir_graph->ifield_lowering_infos_.push_back(field_info);
303 }
304 }
305
306 template <size_t count>
307 void PrepareIFields(const IFieldDef (&defs)[count]) {
308 DoPrepareIFields(defs, count);
309 }
310
311 void PerformNullCheckElimination() {
312 // Make vregs in range [100, 1000) input registers, i.e. requiring a null check.
313 code_item_->registers_size_ = 1000;
314 code_item_->ins_size_ = 900;
315
316 cu_.mir_graph->ComputeDFSOrders();
317 bool gate_result = cu_.mir_graph->EliminateNullChecksGate();
318 ASSERT_TRUE(gate_result);
319 RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get());
320 bool change = false;
321 for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
322 change = cu_.mir_graph->EliminateNullChecks(bb);
323 }
324 cu_.mir_graph->EliminateNullChecksEnd();
325 }
326
327 NullCheckEliminationTest()
328 : MirOptimizationTest() {
329 }
330};
331
332#define DEF_SGET_SPUT_V0(bb, opcode, field_info) \
333 { bb, opcode, field_info, 0u, 0u, 0u }
334
Vladimir Markobfea9c22014-01-17 17:49:33 +0000335TEST_F(ClassInitCheckEliminationTest, SingleBlock) {
336 static const SFieldDef sfields[] = {
337 { 0u, 1u, 0u, 0u },
338 { 1u, 1u, 1u, 1u },
339 { 2u, 1u, 2u, 2u },
340 { 3u, 1u, 3u, 3u }, // Same declaring class as sfield[4].
341 { 4u, 1u, 3u, 4u }, // Same declaring class as sfield[3].
342 { 5u, 0u, 0u, 0u }, // Unresolved.
343 };
Vladimir Markobfea9c22014-01-17 17:49:33 +0000344 static const MIRDef mirs[] = {
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100345 DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 5u), // Unresolved.
346 DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 0u),
347 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u),
348 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u),
349 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 5u), // Unresolved.
350 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u),
351 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u),
352 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u),
353 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 5u), // Unresolved.
354 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 3u),
355 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 4u),
Vladimir Markobfea9c22014-01-17 17:49:33 +0000356 };
357 static const bool expected_ignore_clinit_check[] = {
Vladimir Markof418f322014-07-09 14:45:36 +0100358 false, false, false, false, true, true, true, true, true, false, true
Vladimir Markobfea9c22014-01-17 17:49:33 +0000359 };
360
361 PrepareSFields(sfields);
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100362 PrepareSingleBlock();
Vladimir Markobfea9c22014-01-17 17:49:33 +0000363 PrepareMIRs(mirs);
364 PerformClassInitCheckElimination();
365 ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
366 for (size_t i = 0u; i != arraysize(mirs); ++i) {
367 EXPECT_EQ(expected_ignore_clinit_check[i],
368 (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
369 }
370}
371
372TEST_F(ClassInitCheckEliminationTest, Diamond) {
373 static const SFieldDef sfields[] = {
374 { 0u, 1u, 0u, 0u },
375 { 1u, 1u, 1u, 1u },
376 { 2u, 1u, 2u, 2u },
377 { 3u, 1u, 3u, 3u },
378 { 4u, 1u, 4u, 4u },
379 { 5u, 1u, 5u, 5u },
380 { 6u, 1u, 6u, 6u },
381 { 7u, 1u, 7u, 7u },
382 { 8u, 1u, 8u, 8u }, // Same declaring class as sfield[9].
383 { 9u, 1u, 8u, 9u }, // Same declaring class as sfield[8].
384 { 10u, 0u, 0u, 0u }, // Unresolved.
385 };
Vladimir Markobfea9c22014-01-17 17:49:33 +0000386 static const MIRDef mirs[] = {
387 // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100388 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 10u), // Unresolved.
389 DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 10u), // Unresolved.
390 DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 0u),
391 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 0u), // Eliminated (BB #3 dominates #6).
392 DEF_SGET_SPUT_V0(4u, Instruction::SPUT, 1u),
393 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 1u), // Not eliminated (BB #4 doesn't dominate #6).
394 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u),
395 DEF_SGET_SPUT_V0(4u, Instruction::SGET, 2u), // Eliminated (BB #3 dominates #4).
396 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 3u),
397 DEF_SGET_SPUT_V0(5u, Instruction::SGET, 3u), // Eliminated (BB #3 dominates #5).
398 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 4u),
399 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 4u), // Eliminated (BB #3 dominates #6).
400 DEF_SGET_SPUT_V0(4u, Instruction::SGET, 5u),
401 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 5u), // Not eliminated (BB #4 doesn't dominate #6).
402 DEF_SGET_SPUT_V0(5u, Instruction::SGET, 6u),
403 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 6u), // Not eliminated (BB #5 doesn't dominate #6).
404 DEF_SGET_SPUT_V0(4u, Instruction::SGET, 7u),
405 DEF_SGET_SPUT_V0(5u, Instruction::SGET, 7u),
406 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 7u), // Eliminated (initialized in both #3 and #4).
407 DEF_SGET_SPUT_V0(4u, Instruction::SGET, 8u),
408 DEF_SGET_SPUT_V0(5u, Instruction::SGET, 9u),
409 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 8u), // Eliminated (with sfield[9] in BB #5).
410 DEF_SGET_SPUT_V0(6u, Instruction::SPUT, 9u), // Eliminated (with sfield[8] in BB #4).
Vladimir Markobfea9c22014-01-17 17:49:33 +0000411 };
412 static const bool expected_ignore_clinit_check[] = {
Vladimir Markof418f322014-07-09 14:45:36 +0100413 false, true, // Unresolved: sfield[10], method[2]
Vladimir Markobfea9c22014-01-17 17:49:33 +0000414 false, true, // sfield[0]
415 false, false, // sfield[1]
416 false, true, // sfield[2]
417 false, true, // sfield[3]
418 false, true, // sfield[4]
419 false, false, // sfield[5]
420 false, false, // sfield[6]
421 false, false, true, // sfield[7]
422 false, false, true, true, // sfield[8], sfield[9]
423 };
424
425 PrepareSFields(sfields);
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100426 PrepareDiamond();
Vladimir Markobfea9c22014-01-17 17:49:33 +0000427 PrepareMIRs(mirs);
428 PerformClassInitCheckElimination();
429 ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
430 for (size_t i = 0u; i != arraysize(mirs); ++i) {
431 EXPECT_EQ(expected_ignore_clinit_check[i],
432 (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
433 }
434}
435
436TEST_F(ClassInitCheckEliminationTest, Loop) {
437 static const SFieldDef sfields[] = {
438 { 0u, 1u, 0u, 0u },
439 { 1u, 1u, 1u, 1u },
440 };
Vladimir Markobfea9c22014-01-17 17:49:33 +0000441 static const MIRDef mirs[] = {
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100442 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u),
443 DEF_SGET_SPUT_V0(4u, Instruction::SGET, 1u),
444 DEF_SGET_SPUT_V0(5u, Instruction::SGET, 0u), // Eliminated.
445 DEF_SGET_SPUT_V0(5u, Instruction::SGET, 1u), // Eliminated.
Vladimir Markobfea9c22014-01-17 17:49:33 +0000446 };
447 static const bool expected_ignore_clinit_check[] = {
448 false, false, true, true
449 };
450
451 PrepareSFields(sfields);
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100452 PrepareLoop();
Vladimir Markobfea9c22014-01-17 17:49:33 +0000453 PrepareMIRs(mirs);
454 PerformClassInitCheckElimination();
455 ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
456 for (size_t i = 0u; i != arraysize(mirs); ++i) {
457 EXPECT_EQ(expected_ignore_clinit_check[i],
458 (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
459 }
460}
461
462TEST_F(ClassInitCheckEliminationTest, Catch) {
463 static const SFieldDef sfields[] = {
464 { 0u, 1u, 0u, 0u },
465 { 1u, 1u, 1u, 1u },
Vladimir Marko0a810d22014-07-11 14:44:36 +0100466 { 2u, 1u, 2u, 2u },
467 { 3u, 1u, 3u, 3u },
Vladimir Markobfea9c22014-01-17 17:49:33 +0000468 };
Vladimir Markobfea9c22014-01-17 17:49:33 +0000469 static const MIRDef mirs[] = {
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100470 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u), // Before the exception edge.
471 DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u), // Before the exception edge.
472 DEF_SGET_SPUT_V0(4u, Instruction::SGET, 2u), // After the exception edge.
473 DEF_SGET_SPUT_V0(4u, Instruction::SGET, 3u), // After the exception edge.
474 DEF_SGET_SPUT_V0(5u, Instruction::SGET, 0u), // In catch handler; clinit check eliminated.
475 DEF_SGET_SPUT_V0(5u, Instruction::SGET, 2u), // In catch handler; clinit check not eliminated.
476 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 0u), // Class init check eliminated.
477 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 1u), // Class init check eliminated.
478 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 2u), // Class init check eliminated.
479 DEF_SGET_SPUT_V0(6u, Instruction::SGET, 3u), // Class init check not eliminated.
Vladimir Markobfea9c22014-01-17 17:49:33 +0000480 };
481 static const bool expected_ignore_clinit_check[] = {
Vladimir Marko0a810d22014-07-11 14:44:36 +0100482 false, false, false, false, true, false, true, true, true, false
Vladimir Markobfea9c22014-01-17 17:49:33 +0000483 };
484
485 PrepareSFields(sfields);
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100486 PrepareCatch();
Vladimir Markobfea9c22014-01-17 17:49:33 +0000487 PrepareMIRs(mirs);
488 PerformClassInitCheckElimination();
489 ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
490 for (size_t i = 0u; i != arraysize(mirs); ++i) {
491 EXPECT_EQ(expected_ignore_clinit_check[i],
492 (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
493 }
494}
495
Vladimir Marko7baa6f82014-10-09 18:01:24 +0100496#define DEF_IGET_IPUT(bb, opcode, vA, vB, field_info) \
497 { bb, opcode, field_info, vA, vB, 0u }
498#define DEF_AGET_APUT(bb, opcode, vA, vB, vC) \
499 { bb, opcode, 0u, vA, vB, vC }
500#define DEF_INVOKE(bb, opcode, vC) \
501 { bb, opcode, 0u, 0u, 0u, vC }
502#define DEF_OTHER1(bb, opcode, vA) \
503 { bb, opcode, 0u, vA, 0u, 0u }
504#define DEF_OTHER2(bb, opcode, vA, vB) \
505 { bb, opcode, 0u, vA, vB, 0u }
506
507TEST_F(NullCheckEliminationTest, SingleBlock) {
508 static const IFieldDef ifields[] = {
509 { 0u, 1u, 0u, 0u },
510 { 1u, 1u, 0u, 1u },
511 { 2u, 1u, 0u, 2u }, // Object.
512 };
513 static const MIRDef mirs[] = {
514 DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 0u, 100u, 2u),
515 DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 0u, 1u),
516 DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 2u, 100u, 2u), // Differs from 0u (no LVN here).
517 DEF_IGET_IPUT(3u, Instruction::IGET, 3u, 2u, 1u),
518 DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 101u, 0u),
519 DEF_IGET_IPUT(3u, Instruction::IGET, 5u, 102u, 0u),
520 DEF_IGET_IPUT(3u, Instruction::IGET, 6u, 103u, 0u),
521 DEF_IGET_IPUT(3u, Instruction::IGET, 7u, 103u, 1u),
522 DEF_IGET_IPUT(3u, Instruction::IPUT, 8u, 104u, 0u),
523 DEF_IGET_IPUT(3u, Instruction::IPUT, 9u, 104u, 1u),
524 DEF_IGET_IPUT(3u, Instruction::IGET, 10u, 105u, 0u),
525 DEF_IGET_IPUT(3u, Instruction::IPUT, 11u, 105u, 1u),
526 DEF_IGET_IPUT(3u, Instruction::IPUT, 12u, 106u, 0u),
527 DEF_IGET_IPUT(3u, Instruction::IGET, 13u, 106u, 1u),
528 DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 107),
529 DEF_IGET_IPUT(3u, Instruction::IGET, 15u, 107u, 1u),
530 DEF_IGET_IPUT(3u, Instruction::IGET, 16u, 108u, 0u),
531 DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 108),
532 DEF_AGET_APUT(3u, Instruction::AGET, 18u, 109u, 110u),
533 DEF_AGET_APUT(3u, Instruction::APUT, 19u, 109u, 111u),
534 DEF_OTHER2(3u, Instruction::ARRAY_LENGTH, 20u, 112u),
535 DEF_AGET_APUT(3u, Instruction::AGET, 21u, 112u, 113u),
536 DEF_OTHER1(3u, Instruction::MONITOR_ENTER, 114u),
537 DEF_OTHER1(3u, Instruction::MONITOR_EXIT, 114u),
538 };
539 static const bool expected_ignore_null_check[] = {
540 false, false, true, false /* Not doing LVN. */,
541 false, true /* Set before running NCE. */,
542 false, true, // IGET, IGET
543 false, true, // IPUT, IPUT
544 false, true, // IGET, IPUT
545 false, true, // IPUT, IGET
546 false, true, // INVOKE, IGET
547 false, true, // IGET, INVOKE
548 false, true, // AGET, APUT
549 false, true, // ARRAY_LENGTH, AGET
550 false, true, // MONITOR_ENTER, MONITOR_EXIT
551 };
552
553 PrepareIFields(ifields);
554 PrepareSingleBlock();
555 PrepareMIRs(mirs);
556
557 // Mark IGET 5u as null-checked to test that NCE doesn't clear this flag.
558 mirs_[5u].optimization_flags |= MIR_IGNORE_NULL_CHECK;
559
560 PerformNullCheckElimination();
561 ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
562 for (size_t i = 0u; i != arraysize(mirs); ++i) {
563 EXPECT_EQ(expected_ignore_null_check[i],
564 (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
565 }
566}
567
568TEST_F(NullCheckEliminationTest, Diamond) {
569 static const IFieldDef ifields[] = {
570 { 0u, 1u, 0u, 0u },
571 { 1u, 1u, 0u, 1u },
572 { 2u, 1u, 0u, 2u }, // int[].
573 };
574 static const MIRDef mirs[] = {
575 // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
576 DEF_IGET_IPUT(3u, Instruction::IPUT, 0u, 100u, 0u),
577 DEF_IGET_IPUT(6u, Instruction::IGET, 1u, 100u, 1u), // Eliminated (BB #3 dominates #6).
578 DEF_IGET_IPUT(3u, Instruction::IGET, 2u, 101u, 0u),
579 DEF_IGET_IPUT(4u, Instruction::IPUT, 3u, 101u, 0u), // Eliminated (BB #3 dominates #4).
580 DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u),
581 DEF_IGET_IPUT(5u, Instruction::IPUT, 5u, 102u, 1u), // Eliminated (BB #3 dominates #5).
582 DEF_IGET_IPUT(4u, Instruction::IPUT, 6u, 103u, 0u),
583 DEF_IGET_IPUT(6u, Instruction::IPUT, 7u, 103u, 1u), // Not eliminated (going through BB #5).
584 DEF_IGET_IPUT(5u, Instruction::IGET, 8u, 104u, 1u),
585 DEF_IGET_IPUT(6u, Instruction::IGET, 9u, 104u, 0u), // Not eliminated (going through BB #4).
586 DEF_INVOKE(4u, Instruction::INVOKE_DIRECT, 105u),
587 DEF_IGET_IPUT(5u, Instruction::IGET, 11u, 105u, 1u),
588 DEF_IGET_IPUT(6u, Instruction::IPUT, 12u, 105u, 0u), // Eliminated.
589 DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 13u, 106u, 2u),
590 DEF_OTHER1(3u, Instruction::IF_EQZ, 13u), // Last insn in the BB #3.
591 DEF_OTHER2(5u, Instruction::NEW_ARRAY, 13u, 107u),
592 DEF_AGET_APUT(6u, Instruction::AGET, 16u, 13u, 108u), // Eliminated.
593 };
594 static const bool expected_ignore_null_check[] = {
595 false, true, // BB #3 IPUT, BB #6 IGET
596 false, true, // BB #3 IGET, BB #4 IPUT
597 false, true, // BB #3 IGET, BB #5 IPUT
598 false, false, // BB #4 IPUT, BB #6 IPUT
599 false, false, // BB #5 IGET, BB #6 IGET
600 false, false, true, // BB #4 INVOKE, BB #5 IGET, BB #6 IPUT
601 false, false, // BB #3 IGET_OBJECT & IF_EQZ
602 false, true, // BB #5 NEW_ARRAY, BB #6 AGET
603 };
604
605 PrepareIFields(ifields);
606 PrepareDiamond();
607 PrepareMIRs(mirs);
608 PerformNullCheckElimination();
609 ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
610 for (size_t i = 0u; i != arraysize(mirs); ++i) {
611 EXPECT_EQ(expected_ignore_null_check[i],
612 (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
613 }
614}
615
616TEST_F(NullCheckEliminationTest, Loop) {
617 static const IFieldDef ifields[] = {
618 { 0u, 1u, 0u, 0u },
619 { 1u, 1u, 1u, 1u },
620 };
621 static const MIRDef mirs[] = {
622 DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u),
623 DEF_IGET_IPUT(4u, Instruction::IGET, 1u, 101u, 0u),
624 DEF_IGET_IPUT(5u, Instruction::IGET, 2u, 100u, 1u), // Eliminated.
625 DEF_IGET_IPUT(5u, Instruction::IGET, 3u, 101u, 1u), // Eliminated.
626 DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u),
627 DEF_IGET_IPUT(4u, Instruction::IGET, 5u, 102u, 1u), // Not eliminated (MOVE_OBJECT_16).
628 DEF_OTHER2(4u, Instruction::MOVE_OBJECT_16, 102u, 103u),
629 };
630 static const bool expected_ignore_null_check[] = {
631 false, false, true, true,
632 false, false, false,
633 };
634
635 PrepareIFields(ifields);
636 PrepareLoop();
637 PrepareMIRs(mirs);
638 PerformNullCheckElimination();
639 ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
640 for (size_t i = 0u; i != arraysize(mirs); ++i) {
641 EXPECT_EQ(expected_ignore_null_check[i],
642 (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
643 }
644}
645
646TEST_F(NullCheckEliminationTest, Catch) {
647 static const IFieldDef ifields[] = {
648 { 0u, 1u, 0u, 0u },
649 { 1u, 1u, 1u, 1u },
650 };
651 static const MIRDef mirs[] = {
652 DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u), // Before the exception edge.
653 DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 101u, 0u), // Before the exception edge.
654 DEF_IGET_IPUT(4u, Instruction::IGET, 2u, 102u, 0u), // After the exception edge.
655 DEF_IGET_IPUT(4u, Instruction::IGET, 3u, 103u, 0u), // After the exception edge.
656 DEF_IGET_IPUT(5u, Instruction::IGET, 4u, 100u, 1u), // In catch handler; eliminated.
657 DEF_IGET_IPUT(5u, Instruction::IGET, 5u, 102u, 1u), // In catch handler; not eliminated.
658 DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 100u, 0u), // Null check eliminated.
659 DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 101u, 1u), // Null check eliminated.
660 DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 102u, 0u), // Null check eliminated.
661 DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 103u, 1u), // Null check not eliminated.
662 };
663 static const bool expected_ignore_null_check[] = {
664 false, false, false, false, true, false, true, true, true, false
665 };
666
667 PrepareIFields(ifields);
668 PrepareCatch();
669 PrepareMIRs(mirs);
670 PerformNullCheckElimination();
671 ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
672 for (size_t i = 0u; i != arraysize(mirs); ++i) {
673 EXPECT_EQ(expected_ignore_null_check[i],
674 (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
675 }
676}
677
678// Undefine MIR_DEF for null check elimination.
679#undef MIR_DEF
680
Vladimir Markobfea9c22014-01-17 17:49:33 +0000681} // namespace art