blob: f71a4734a63df73455166047a7352998773e6653 [file] [log] [blame]
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001/*
2 * Copyright (C) 2019 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 <tuple>
18
19#include "load_store_analysis.h"
20#include "load_store_elimination.h"
21#include "nodes.h"
22#include "optimizing_unit_test.h"
xueliang.zhongd71f1dc2018-01-24 17:24:16 +000023
24#include "gtest/gtest.h"
25
26namespace art {
27
Vladimir Marko5d2311a2020-05-13 17:30:32 +010028class LoadStoreEliminationTest : public OptimizingUnitTest {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +000029 public:
30 void PerformLSE() {
31 graph_->BuildDominatorTree();
Vladimir Marko3224f382020-06-23 14:19:53 +010032 LoadStoreElimination lse(graph_, /*stats=*/ nullptr);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +000033 lse.Run();
34 EXPECT_TRUE(CheckGraphSkipRefTypeInfoChecks());
35 }
36
37 // Create instructions shared among tests.
38 void CreateEntryBlockInstructions() {
39 HInstruction* c1 = graph_->GetIntConstant(1);
40 HInstruction* c4 = graph_->GetIntConstant(4);
41 i_add1_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c1);
42 i_add4_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c4);
43 entry_block_->AddInstruction(i_add1_);
44 entry_block_->AddInstruction(i_add4_);
45 entry_block_->AddInstruction(new (GetAllocator()) HGoto());
46 }
47
48 // Create the major CFG used by tests:
49 // entry
50 // |
51 // pre_header
52 // |
53 // loop[]
54 // |
55 // return
56 // |
57 // exit
58 void CreateTestControlFlowGraph() {
Vladimir Marko5d2311a2020-05-13 17:30:32 +010059 InitGraphAndParameters();
60 pre_header_ = AddNewBlock();
61 loop_ = AddNewBlock();
xueliang.zhongd71f1dc2018-01-24 17:24:16 +000062
63 entry_block_->ReplaceSuccessor(return_block_, pre_header_);
64 pre_header_->AddSuccessor(loop_);
65 loop_->AddSuccessor(loop_);
66 loop_->AddSuccessor(return_block_);
67
68 HInstruction* c0 = graph_->GetIntConstant(0);
69 HInstruction* c1 = graph_->GetIntConstant(1);
70 HInstruction* c128 = graph_->GetIntConstant(128);
71
72 CreateEntryBlockInstructions();
73
74 // pre_header block
75 // phi = 0;
76 phi_ = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
77 loop_->AddPhi(phi_);
78 pre_header_->AddInstruction(new (GetAllocator()) HGoto());
79 phi_->AddInput(c0);
80
81 // loop block:
82 // suspend_check
83 // phi++;
84 // if (phi >= 128)
85 suspend_check_ = new (GetAllocator()) HSuspendCheck();
86 HInstruction* inc_phi = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi_, c1);
87 HInstruction* cmp = new (GetAllocator()) HGreaterThanOrEqual(phi_, c128);
88 HInstruction* hif = new (GetAllocator()) HIf(cmp);
89 loop_->AddInstruction(suspend_check_);
90 loop_->AddInstruction(inc_phi);
91 loop_->AddInstruction(cmp);
92 loop_->AddInstruction(hif);
93 phi_->AddInput(inc_phi);
94
95 CreateEnvForSuspendCheck();
96 }
97
98 void CreateEnvForSuspendCheck() {
99 ArenaVector<HInstruction*> current_locals({array_, i_, j_},
100 GetAllocator()->Adapter(kArenaAllocInstruction));
101 ManuallyBuildEnvFor(suspend_check_, &current_locals);
102 }
103
104 // Create the diamond-shaped CFG:
105 // upper
106 // / \
107 // left right
108 // \ /
109 // down
110 //
111 // Return: the basic blocks forming the CFG in the following order {upper, left, right, down}.
112 std::tuple<HBasicBlock*, HBasicBlock*, HBasicBlock*, HBasicBlock*> CreateDiamondShapedCFG() {
Vladimir Marko5d2311a2020-05-13 17:30:32 +0100113 InitGraphAndParameters();
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000114 CreateEntryBlockInstructions();
115
Vladimir Marko5d2311a2020-05-13 17:30:32 +0100116 HBasicBlock* upper = AddNewBlock();
117 HBasicBlock* left = AddNewBlock();
118 HBasicBlock* right = AddNewBlock();
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000119
120 entry_block_->ReplaceSuccessor(return_block_, upper);
121 upper->AddSuccessor(left);
122 upper->AddSuccessor(right);
123 left->AddSuccessor(return_block_);
124 right->AddSuccessor(return_block_);
125
126 HInstruction* cmp = new (GetAllocator()) HGreaterThanOrEqual(i_, j_);
127 HInstruction* hif = new (GetAllocator()) HIf(cmp);
128 upper->AddInstruction(cmp);
129 upper->AddInstruction(hif);
130
131 left->AddInstruction(new (GetAllocator()) HGoto());
132 right->AddInstruction(new (GetAllocator()) HGoto());
133
134 return std::make_tuple(upper, left, right, return_block_);
135 }
136
137 // Add a HVecLoad instruction to the end of the provided basic block.
138 //
139 // Return: the created HVecLoad instruction.
140 HInstruction* AddVecLoad(HBasicBlock* block, HInstruction* array, HInstruction* index) {
141 DCHECK(block != nullptr);
142 DCHECK(array != nullptr);
143 DCHECK(index != nullptr);
144 HInstruction* vload = new (GetAllocator()) HVecLoad(
145 GetAllocator(),
146 array,
147 index,
148 DataType::Type::kInt32,
149 SideEffects::ArrayReadOfType(DataType::Type::kInt32),
150 4,
151 /*is_string_char_at*/ false,
152 kNoDexPc);
153 block->InsertInstructionBefore(vload, block->GetLastInstruction());
154 return vload;
155 }
156
157 // Add a HVecStore instruction to the end of the provided basic block.
158 // If no vdata is specified, generate HVecStore: array[index] = [1,1,1,1].
159 //
160 // Return: the created HVecStore instruction.
161 HInstruction* AddVecStore(HBasicBlock* block,
162 HInstruction* array,
163 HInstruction* index,
164 HInstruction* vdata = nullptr) {
165 DCHECK(block != nullptr);
166 DCHECK(array != nullptr);
167 DCHECK(index != nullptr);
168 if (vdata == nullptr) {
169 HInstruction* c1 = graph_->GetIntConstant(1);
170 vdata = new (GetAllocator()) HVecReplicateScalar(GetAllocator(),
171 c1,
172 DataType::Type::kInt32,
173 4,
174 kNoDexPc);
175 block->InsertInstructionBefore(vdata, block->GetLastInstruction());
176 }
177 HInstruction* vstore = new (GetAllocator()) HVecStore(
178 GetAllocator(),
179 array,
180 index,
181 vdata,
182 DataType::Type::kInt32,
183 SideEffects::ArrayWriteOfType(DataType::Type::kInt32),
184 4,
185 kNoDexPc);
186 block->InsertInstructionBefore(vstore, block->GetLastInstruction());
187 return vstore;
188 }
189
190 // Add a HArrayGet instruction to the end of the provided basic block.
191 //
192 // Return: the created HArrayGet instruction.
193 HInstruction* AddArrayGet(HBasicBlock* block, HInstruction* array, HInstruction* index) {
194 DCHECK(block != nullptr);
195 DCHECK(array != nullptr);
196 DCHECK(index != nullptr);
197 HInstruction* get = new (GetAllocator()) HArrayGet(array, index, DataType::Type::kInt32, 0);
198 block->InsertInstructionBefore(get, block->GetLastInstruction());
199 return get;
200 }
201
202 // Add a HArraySet instruction to the end of the provided basic block.
203 // If no data is specified, generate HArraySet: array[index] = 1.
204 //
205 // Return: the created HArraySet instruction.
206 HInstruction* AddArraySet(HBasicBlock* block,
207 HInstruction* array,
208 HInstruction* index,
209 HInstruction* data = nullptr) {
210 DCHECK(block != nullptr);
211 DCHECK(array != nullptr);
212 DCHECK(index != nullptr);
213 if (data == nullptr) {
214 data = graph_->GetIntConstant(1);
215 }
216 HInstruction* store = new (GetAllocator()) HArraySet(array,
217 index,
218 data,
219 DataType::Type::kInt32,
220 0);
221 block->InsertInstructionBefore(store, block->GetLastInstruction());
222 return store;
223 }
224
Vladimir Marko5d2311a2020-05-13 17:30:32 +0100225 void InitGraphAndParameters() {
226 InitGraph();
227 AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
228 dex::TypeIndex(0),
229 0,
230 DataType::Type::kInt32));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000231 array_ = parameters_.back();
Vladimir Marko5d2311a2020-05-13 17:30:32 +0100232 AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
233 dex::TypeIndex(1),
234 1,
235 DataType::Type::kInt32));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000236 i_ = parameters_.back();
Vladimir Marko5d2311a2020-05-13 17:30:32 +0100237 AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
238 dex::TypeIndex(1),
239 2,
240 DataType::Type::kInt32));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000241 j_ = parameters_.back();
242 }
243
244 HBasicBlock* pre_header_;
245 HBasicBlock* loop_;
246
247 HInstruction* array_;
248 HInstruction* i_;
249 HInstruction* j_;
250 HInstruction* i_add1_;
251 HInstruction* i_add4_;
252 HInstruction* suspend_check_;
253
254 HPhi* phi_;
255};
256
257TEST_F(LoadStoreEliminationTest, ArrayGetSetElimination) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000258 CreateTestControlFlowGraph();
259
260 HInstruction* c1 = graph_->GetIntConstant(1);
261 HInstruction* c2 = graph_->GetIntConstant(2);
262 HInstruction* c3 = graph_->GetIntConstant(3);
263
264 // array[1] = 1;
265 // x = array[1]; <--- Remove.
266 // y = array[2];
267 // array[1] = 1; <--- Remove, since it stores same value.
268 // array[i] = 3; <--- MAY alias.
269 // array[1] = 1; <--- Cannot remove, even if it stores the same value.
270 AddArraySet(entry_block_, array_, c1, c1);
271 HInstruction* load1 = AddArrayGet(entry_block_, array_, c1);
272 HInstruction* load2 = AddArrayGet(entry_block_, array_, c2);
273 HInstruction* store1 = AddArraySet(entry_block_, array_, c1, c1);
274 AddArraySet(entry_block_, array_, i_, c3);
275 HInstruction* store2 = AddArraySet(entry_block_, array_, c1, c1);
276
277 PerformLSE();
278
279 ASSERT_TRUE(IsRemoved(load1));
280 ASSERT_FALSE(IsRemoved(load2));
281 ASSERT_TRUE(IsRemoved(store1));
282 ASSERT_FALSE(IsRemoved(store2));
283}
284
285TEST_F(LoadStoreEliminationTest, SameHeapValue1) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000286 CreateTestControlFlowGraph();
287
288 HInstruction* c1 = graph_->GetIntConstant(1);
289 HInstruction* c2 = graph_->GetIntConstant(2);
290
291 // Test LSE handling same value stores on array.
292 // array[1] = 1;
293 // array[2] = 1;
294 // array[1] = 1; <--- Can remove.
295 // array[1] = 2; <--- Can NOT remove.
296 AddArraySet(entry_block_, array_, c1, c1);
297 AddArraySet(entry_block_, array_, c2, c1);
298 HInstruction* store1 = AddArraySet(entry_block_, array_, c1, c1);
299 HInstruction* store2 = AddArraySet(entry_block_, array_, c1, c2);
300
301 PerformLSE();
302
303 ASSERT_TRUE(IsRemoved(store1));
304 ASSERT_FALSE(IsRemoved(store2));
305}
306
307TEST_F(LoadStoreEliminationTest, SameHeapValue2) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000308 CreateTestControlFlowGraph();
309
310 // Test LSE handling same value stores on vector.
311 // vdata = [0x1, 0x2, 0x3, 0x4, ...]
312 // VecStore array[i...] = vdata;
313 // VecStore array[j...] = vdata; <--- MAY ALIAS.
314 // VecStore array[i...] = vdata; <--- Cannot Remove, even if it's same value.
315 AddVecStore(entry_block_, array_, i_);
316 AddVecStore(entry_block_, array_, j_);
317 HInstruction* vstore = AddVecStore(entry_block_, array_, i_);
318
319 PerformLSE();
320
321 ASSERT_FALSE(IsRemoved(vstore));
322}
323
324TEST_F(LoadStoreEliminationTest, SameHeapValue3) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000325 CreateTestControlFlowGraph();
326
327 // VecStore array[i...] = vdata;
328 // VecStore array[i+1...] = vdata; <--- MAY alias due to partial overlap.
329 // VecStore array[i...] = vdata; <--- Cannot remove, even if it's same value.
330 AddVecStore(entry_block_, array_, i_);
331 AddVecStore(entry_block_, array_, i_add1_);
332 HInstruction* vstore = AddVecStore(entry_block_, array_, i_);
333
334 PerformLSE();
335
336 ASSERT_FALSE(IsRemoved(vstore));
337}
338
339TEST_F(LoadStoreEliminationTest, OverlappingLoadStore) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000340 CreateTestControlFlowGraph();
341
342 HInstruction* c1 = graph_->GetIntConstant(1);
343
344 // Test LSE handling array LSE when there is vector store in between.
345 // a[i] = 1;
346 // .. = a[i]; <-- Remove.
347 // a[i,i+1,i+2,i+3] = data; <-- PARTIAL OVERLAP !
348 // .. = a[i]; <-- Cannot remove.
349 AddArraySet(entry_block_, array_, i_, c1);
350 HInstruction* load1 = AddArrayGet(entry_block_, array_, i_);
351 AddVecStore(entry_block_, array_, i_);
352 HInstruction* load2 = AddArrayGet(entry_block_, array_, i_);
353
354 // Test LSE handling vector load/store partial overlap.
355 // a[i,i+1,i+2,i+3] = data;
356 // a[i+4,i+5,i+6,i+7] = data;
357 // .. = a[i,i+1,i+2,i+3];
358 // .. = a[i+4,i+5,i+6,i+7];
359 // a[i+1,i+2,i+3,i+4] = data; <-- PARTIAL OVERLAP !
360 // .. = a[i,i+1,i+2,i+3];
361 // .. = a[i+4,i+5,i+6,i+7];
362 AddVecStore(entry_block_, array_, i_);
363 AddVecStore(entry_block_, array_, i_add4_);
364 HInstruction* vload1 = AddVecLoad(entry_block_, array_, i_);
365 HInstruction* vload2 = AddVecLoad(entry_block_, array_, i_add4_);
366 AddVecStore(entry_block_, array_, i_add1_);
367 HInstruction* vload3 = AddVecLoad(entry_block_, array_, i_);
368 HInstruction* vload4 = AddVecLoad(entry_block_, array_, i_add4_);
369
370 // Test LSE handling vector LSE when there is array store in between.
371 // a[i,i+1,i+2,i+3] = data;
372 // a[i+1] = 1; <-- PARTIAL OVERLAP !
373 // .. = a[i,i+1,i+2,i+3];
374 AddVecStore(entry_block_, array_, i_);
375 AddArraySet(entry_block_, array_, i_, c1);
376 HInstruction* vload5 = AddVecLoad(entry_block_, array_, i_);
377
378 PerformLSE();
379
380 ASSERT_TRUE(IsRemoved(load1));
381 ASSERT_FALSE(IsRemoved(load2));
382
383 ASSERT_TRUE(IsRemoved(vload1));
384 ASSERT_TRUE(IsRemoved(vload2));
385 ASSERT_FALSE(IsRemoved(vload3));
386 ASSERT_FALSE(IsRemoved(vload4));
387
388 ASSERT_FALSE(IsRemoved(vload5));
389}
390// function (int[] a, int j) {
391// a[j] = 1;
392// for (int i=0; i<128; i++) {
393// /* doesn't do any write */
394// }
395// a[j] = 1;
396TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithoutSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000397 CreateTestControlFlowGraph();
398
399 HInstruction* c1 = graph_->GetIntConstant(1);
400
401 // a[j] = 1
402 AddArraySet(pre_header_, array_, j_, c1);
403
404 // LOOP BODY:
405 // .. = a[i,i+1,i+2,i+3];
406 AddVecLoad(loop_, array_, phi_);
407
408 // a[j] = 1;
409 HInstruction* array_set = AddArraySet(return_block_, array_, j_, c1);
410
411 PerformLSE();
412
413 ASSERT_TRUE(IsRemoved(array_set));
414}
415
416// function (int[] a, int j) {
417// int[] b = new int[128];
418// a[j] = 0;
419// for (int phi=0; phi<128; phi++) {
420// a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
421// b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
422// }
423// a[j] = 0;
424// }
425TEST_F(LoadStoreEliminationTest, StoreAfterSIMDLoopWithSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000426 CreateTestControlFlowGraph();
427
428 HInstruction* c0 = graph_->GetIntConstant(0);
429 HInstruction* c128 = graph_->GetIntConstant(128);
430
431 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
432 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
433 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
434
435 // a[j] = 0;
436 AddArraySet(pre_header_, array_, j_, c0);
437
438 // LOOP BODY:
439 // a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
440 // b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
441 AddVecStore(loop_, array_, phi_);
442 HInstruction* vload = AddVecLoad(loop_, array_, phi_);
443 AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
444
445 // a[j] = 0;
446 HInstruction* a_set = AddArraySet(return_block_, array_, j_, c0);
447
448 PerformLSE();
449
450 ASSERT_TRUE(IsRemoved(vload));
451 ASSERT_FALSE(IsRemoved(a_set)); // Cannot remove due to write side-effect in the loop.
452}
453
454// function (int[] a, int j) {
455// int[] b = new int[128];
456// a[j] = 0;
457// for (int phi=0; phi<128; phi++) {
458// a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
459// b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
460// }
461// x = a[j];
462// }
463TEST_F(LoadStoreEliminationTest, LoadAfterSIMDLoopWithSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000464 CreateTestControlFlowGraph();
465
466 HInstruction* c0 = graph_->GetIntConstant(0);
467 HInstruction* c128 = graph_->GetIntConstant(128);
468
469 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
470 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
471 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
472
473 // a[j] = 0;
474 AddArraySet(pre_header_, array_, j_, c0);
475
476 // LOOP BODY:
477 // a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
478 // b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
479 AddVecStore(loop_, array_, phi_);
480 HInstruction* vload = AddVecLoad(loop_, array_, phi_);
481 AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
482
483 // x = a[j];
484 HInstruction* load = AddArrayGet(return_block_, array_, j_);
485
486 PerformLSE();
487
488 ASSERT_TRUE(IsRemoved(vload));
489 ASSERT_FALSE(IsRemoved(load)); // Cannot remove due to write side-effect in the loop.
490}
491
492// Check that merging works correctly when there are VecStors in predecessors.
493//
494// vstore1: a[i,... i + 3] = [1,...1]
495// / \
496// / \
497// vstore2: a[i,... i + 3] = [1,...1] vstore3: a[i+1, ... i + 4] = [1, ... 1]
498// \ /
499// \ /
500// vstore4: a[i,... i + 3] = [1,...1]
501//
502// Expected:
503// 'vstore2' is removed.
504// 'vstore3' is not removed.
505// 'vstore4' is not removed. Such cases are not supported at the moment.
506TEST_F(LoadStoreEliminationTest, MergePredecessorVecStores) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000507 HBasicBlock* upper;
508 HBasicBlock* left;
509 HBasicBlock* right;
510 HBasicBlock* down;
511 std::tie(upper, left, right, down) = CreateDiamondShapedCFG();
512
513 // upper: a[i,... i + 3] = [1,...1]
514 HInstruction* vstore1 = AddVecStore(upper, array_, i_);
515 HInstruction* vdata = vstore1->InputAt(2);
516
517 // left: a[i,... i + 3] = [1,...1]
518 HInstruction* vstore2 = AddVecStore(left, array_, i_, vdata);
519
520 // right: a[i+1, ... i + 4] = [1, ... 1]
521 HInstruction* vstore3 = AddVecStore(right, array_, i_add1_, vdata);
522
523 // down: a[i,... i + 3] = [1,...1]
524 HInstruction* vstore4 = AddVecStore(down, array_, i_, vdata);
525
526 PerformLSE();
527
528 ASSERT_TRUE(IsRemoved(vstore2));
529 ASSERT_FALSE(IsRemoved(vstore3));
530 ASSERT_FALSE(IsRemoved(vstore4));
531}
532
533// Check that merging works correctly when there are ArraySets in predecessors.
534//
535// a[i] = 1
536// / \
537// / \
538// store1: a[i] = 1 store2: a[i+1] = 1
539// \ /
540// \ /
541// store3: a[i] = 1
542//
543// Expected:
544// 'store1' is removed.
545// 'store2' is not removed.
546// 'store3' is removed.
547TEST_F(LoadStoreEliminationTest, MergePredecessorStores) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000548 HBasicBlock* upper;
549 HBasicBlock* left;
550 HBasicBlock* right;
551 HBasicBlock* down;
552 std::tie(upper, left, right, down) = CreateDiamondShapedCFG();
553
554 // upper: a[i,... i + 3] = [1,...1]
555 AddArraySet(upper, array_, i_);
556
557 // left: a[i,... i + 3] = [1,...1]
558 HInstruction* store1 = AddArraySet(left, array_, i_);
559
560 // right: a[i+1, ... i + 4] = [1, ... 1]
561 HInstruction* store2 = AddArraySet(right, array_, i_add1_);
562
563 // down: a[i,... i + 3] = [1,...1]
564 HInstruction* store3 = AddArraySet(down, array_, i_);
565
566 PerformLSE();
567
568 ASSERT_TRUE(IsRemoved(store1));
569 ASSERT_FALSE(IsRemoved(store2));
570 ASSERT_TRUE(IsRemoved(store3));
571}
572
573// Check that redundant VStore/VLoad are removed from a SIMD loop.
574//
575// LOOP BODY
576// vstore1: a[i,... i + 3] = [1,...1]
577// vload: x = a[i,... i + 3]
578// vstore2: b[i,... i + 3] = x
579// vstore3: a[i,... i + 3] = [1,...1]
580//
Vladimir Marko3224f382020-06-23 14:19:53 +0100581// Return 'a' from the method to make it escape.
582//
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000583// Expected:
584// 'vstore1' is not removed.
585// 'vload' is removed.
Vladimir Marko3224f382020-06-23 14:19:53 +0100586// 'vstore2' is removed because 'b' does not escape.
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000587// 'vstore3' is removed.
588TEST_F(LoadStoreEliminationTest, RedundantVStoreVLoadInLoop) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000589 CreateTestControlFlowGraph();
590
591 HInstruction* c0 = graph_->GetIntConstant(0);
592 HInstruction* c128 = graph_->GetIntConstant(128);
593
594 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
595 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
596 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
597
Vladimir Marko3224f382020-06-23 14:19:53 +0100598 ASSERT_TRUE(return_block_->GetLastInstruction()->IsReturnVoid());
599 HInstruction* ret = new (GetAllocator()) HReturn(array_a);
600 return_block_->ReplaceAndRemoveInstructionWith(return_block_->GetLastInstruction(), ret);
601
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000602 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
603 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
604 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
605
606 // LOOP BODY:
607 // a[i,... i + 3] = [1,...1]
608 // x = a[i,... i + 3]
609 // b[i,... i + 3] = x
610 // a[i,... i + 3] = [1,...1]
611 HInstruction* vstore1 = AddVecStore(loop_, array_a, phi_);
612 HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
Vladimir Marko3224f382020-06-23 14:19:53 +0100613 HInstruction* vstore2 = AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000614 HInstruction* vstore3 = AddVecStore(loop_, array_a, phi_, vstore1->InputAt(2));
615
616 PerformLSE();
617
618 ASSERT_FALSE(IsRemoved(vstore1));
619 ASSERT_TRUE(IsRemoved(vload));
Vladimir Marko3224f382020-06-23 14:19:53 +0100620 ASSERT_TRUE(IsRemoved(vstore2));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000621 ASSERT_TRUE(IsRemoved(vstore3));
622}
623
Vladimir Marko3224f382020-06-23 14:19:53 +0100624// Loop writes invalidate only possibly aliased heap locations.
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000625TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000626 CreateTestControlFlowGraph();
627
628 HInstruction* c0 = graph_->GetIntConstant(0);
629 HInstruction* c2 = graph_->GetIntConstant(2);
630 HInstruction* c128 = graph_->GetIntConstant(128);
631
632 // array[0] = 2;
633 // loop:
634 // b[i] = array[i]
635 // array[0] = 2
Vladimir Marko3224f382020-06-23 14:19:53 +0100636 HInstruction* store1 = AddArraySet(entry_block_, array_, c0, c2);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000637
638 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
639 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
640 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
641
642 HInstruction* load = AddArrayGet(loop_, array_, phi_);
Vladimir Marko3224f382020-06-23 14:19:53 +0100643 HInstruction* store2 = AddArraySet(loop_, array_b, phi_, load);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000644
Vladimir Marko3224f382020-06-23 14:19:53 +0100645 HInstruction* store3 = AddArraySet(return_block_, array_, c0, c2);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000646
647 PerformLSE();
648
Vladimir Marko3224f382020-06-23 14:19:53 +0100649 ASSERT_FALSE(IsRemoved(store1));
650 ASSERT_TRUE(IsRemoved(store2));
651 ASSERT_TRUE(IsRemoved(store3));
652}
653
654// Loop writes invalidate only possibly aliased heap locations.
655TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithSideEffects2) {
656 CreateTestControlFlowGraph();
657
658 // Add another array parameter that may alias with `array_`.
659 // Note: We're not adding it to the suspend check environment.
660 AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
661 dex::TypeIndex(0),
662 3,
663 DataType::Type::kInt32));
664 HInstruction* array2 = parameters_.back();
665
666 HInstruction* c0 = graph_->GetIntConstant(0);
667 HInstruction* c2 = graph_->GetIntConstant(2);
668
669 // array[0] = 2;
670 // loop:
671 // array2[i] = array[i]
672 // array[0] = 2
673 HInstruction* store1 = AddArraySet(entry_block_, array_, c0, c2);
674
675 HInstruction* load = AddArrayGet(loop_, array_, phi_);
676 HInstruction* store2 = AddArraySet(loop_, array2, phi_, load);
677
678 HInstruction* store3 = AddArraySet(return_block_, array_, c0, c2);
679
680 PerformLSE();
681
682 ASSERT_FALSE(IsRemoved(store1));
683 ASSERT_FALSE(IsRemoved(store2));
684 ASSERT_FALSE(IsRemoved(store3));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000685}
686
687// As it is not allowed to use defaults for VecLoads, check if there is a new created array
688// a VecLoad used in a loop and after it is not replaced with a default.
689TEST_F(LoadStoreEliminationTest, VLoadDefaultValueInLoopWithoutWriteSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000690 CreateTestControlFlowGraph();
691
692 HInstruction* c0 = graph_->GetIntConstant(0);
693 HInstruction* c128 = graph_->GetIntConstant(128);
694
695 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
696 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
697 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
698
699 // LOOP BODY:
700 // v = a[i,... i + 3]
701 // array[0,... 3] = v
702 HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
703 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
704
705 PerformLSE();
706
707 ASSERT_FALSE(IsRemoved(vload));
708 ASSERT_FALSE(IsRemoved(vstore));
709}
710
711// As it is not allowed to use defaults for VecLoads, check if there is a new created array
712// a VecLoad is not replaced with a default.
713TEST_F(LoadStoreEliminationTest, VLoadDefaultValue) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000714 CreateTestControlFlowGraph();
715
716 HInstruction* c0 = graph_->GetIntConstant(0);
717 HInstruction* c128 = graph_->GetIntConstant(128);
718
719 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
720 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
721 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
722
723 // v = a[0,... 3]
724 // array[0,... 3] = v
725 HInstruction* vload = AddVecLoad(pre_header_, array_a, c0);
726 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
727
728 PerformLSE();
729
730 ASSERT_FALSE(IsRemoved(vload));
731 ASSERT_FALSE(IsRemoved(vstore));
732}
733
734// As it is allowed to use defaults for ordinary loads, check if there is a new created array
735// a load used in a loop and after it is replaced with a default.
736TEST_F(LoadStoreEliminationTest, LoadDefaultValueInLoopWithoutWriteSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000737 CreateTestControlFlowGraph();
738
739 HInstruction* c0 = graph_->GetIntConstant(0);
740 HInstruction* c128 = graph_->GetIntConstant(128);
741
742 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
743 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
744 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
745
746 // LOOP BODY:
747 // v = a[i]
748 // array[0] = v
749 HInstruction* load = AddArrayGet(loop_, array_a, phi_);
750 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
751
752 PerformLSE();
753
754 ASSERT_TRUE(IsRemoved(load));
755 ASSERT_FALSE(IsRemoved(store));
756}
757
758// As it is allowed to use defaults for ordinary loads, check if there is a new created array
759// a load is replaced with a default.
760TEST_F(LoadStoreEliminationTest, LoadDefaultValue) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000761 CreateTestControlFlowGraph();
762
763 HInstruction* c0 = graph_->GetIntConstant(0);
764 HInstruction* c128 = graph_->GetIntConstant(128);
765
766 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
767 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
768 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
769
770 // v = a[0]
771 // array[0] = v
772 HInstruction* load = AddArrayGet(pre_header_, array_a, c0);
773 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
774
775 PerformLSE();
776
777 ASSERT_TRUE(IsRemoved(load));
778 ASSERT_FALSE(IsRemoved(store));
779}
780
781// As it is not allowed to use defaults for VecLoads but allowed for regular loads,
782// check if there is a new created array, a VecLoad and a load used in a loop and after it,
783// VecLoad is not replaced with a default but the load is.
784TEST_F(LoadStoreEliminationTest, VLoadAndLoadDefaultValueInLoopWithoutWriteSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000785 CreateTestControlFlowGraph();
786
787 HInstruction* c0 = graph_->GetIntConstant(0);
788 HInstruction* c128 = graph_->GetIntConstant(128);
789
790 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
791 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
792 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
793
794 // LOOP BODY:
795 // v = a[i,... i + 3]
796 // v1 = a[i]
797 // array[0,... 3] = v
798 // array[0] = v1
799 HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
800 HInstruction* load = AddArrayGet(loop_, array_a, phi_);
801 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
802 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
803
804 PerformLSE();
805
806 ASSERT_FALSE(IsRemoved(vload));
807 ASSERT_TRUE(IsRemoved(load));
808 ASSERT_FALSE(IsRemoved(vstore));
809 ASSERT_FALSE(IsRemoved(store));
810}
811
812// As it is not allowed to use defaults for VecLoads but allowed for regular loads,
813// check if there is a new created array, a VecLoad and a load,
814// VecLoad is not replaced with a default but the load is.
815TEST_F(LoadStoreEliminationTest, VLoadAndLoadDefaultValue) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000816 CreateTestControlFlowGraph();
817
818 HInstruction* c0 = graph_->GetIntConstant(0);
819 HInstruction* c128 = graph_->GetIntConstant(128);
820
821 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
822 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
823 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
824
825 // v = a[0,... 3]
826 // v1 = a[0]
827 // array[0,... 3] = v
828 // array[0] = v1
829 HInstruction* vload = AddVecLoad(pre_header_, array_a, c0);
830 HInstruction* load = AddArrayGet(pre_header_, array_a, c0);
831 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
832 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
833
834 PerformLSE();
835
836 ASSERT_FALSE(IsRemoved(vload));
837 ASSERT_TRUE(IsRemoved(load));
838 ASSERT_FALSE(IsRemoved(vstore));
839 ASSERT_FALSE(IsRemoved(store));
840}
841
842// It is not allowed to use defaults for VecLoads. However it should not prevent from removing
843// loads getting the same value.
844// Check a load getting a known value is eliminated (a loop test case).
845TEST_F(LoadStoreEliminationTest, VLoadDefaultValueAndVLoadInLoopWithoutWriteSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000846 CreateTestControlFlowGraph();
847
848 HInstruction* c0 = graph_->GetIntConstant(0);
849 HInstruction* c128 = graph_->GetIntConstant(128);
850
851 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
852 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
853 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
854
855 // LOOP BODY:
856 // v = a[i,... i + 3]
857 // v1 = a[i,... i + 3]
858 // array[0,... 3] = v
859 // array[128,... 131] = v1
860 HInstruction* vload1 = AddVecLoad(loop_, array_a, phi_);
861 HInstruction* vload2 = AddVecLoad(loop_, array_a, phi_);
862 HInstruction* vstore1 = AddVecStore(return_block_, array_, c0, vload1->AsVecLoad());
863 HInstruction* vstore2 = AddVecStore(return_block_, array_, c128, vload2->AsVecLoad());
864
865 PerformLSE();
866
867 ASSERT_FALSE(IsRemoved(vload1));
868 ASSERT_TRUE(IsRemoved(vload2));
869 ASSERT_FALSE(IsRemoved(vstore1));
870 ASSERT_FALSE(IsRemoved(vstore2));
871}
872
873// It is not allowed to use defaults for VecLoads. However it should not prevent from removing
874// loads getting the same value.
875// Check a load getting a known value is eliminated.
876TEST_F(LoadStoreEliminationTest, VLoadDefaultValueAndVLoad) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000877 CreateTestControlFlowGraph();
878
879 HInstruction* c0 = graph_->GetIntConstant(0);
880 HInstruction* c128 = graph_->GetIntConstant(128);
881
882 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
883 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
884 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
885
886 // v = a[0,... 3]
887 // v1 = a[0,... 3]
888 // array[0,... 3] = v
889 // array[128,... 131] = v1
890 HInstruction* vload1 = AddVecLoad(pre_header_, array_a, c0);
891 HInstruction* vload2 = AddVecLoad(pre_header_, array_a, c0);
892 HInstruction* vstore1 = AddVecStore(return_block_, array_, c0, vload1->AsVecLoad());
893 HInstruction* vstore2 = AddVecStore(return_block_, array_, c128, vload2->AsVecLoad());
894
895 PerformLSE();
896
897 ASSERT_FALSE(IsRemoved(vload1));
898 ASSERT_TRUE(IsRemoved(vload2));
899 ASSERT_FALSE(IsRemoved(vstore1));
900 ASSERT_FALSE(IsRemoved(vstore2));
901}
902
Alex Light9dec90a2020-09-14 17:58:28 -0700903// void DO_CAL() {
904// int i = 1;
905// int[] w = new int[80];
906// int t = 0;
907// while (i < 80) {
908// w[i] = PLEASE_INTERLEAVE(w[i - 1], 1)
909// t = PLEASE_SELECT(w[i], t);
910// i++;
911// }
912// return t;
913// }
914TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) {
915 CreateGraph();
916 AdjacencyListGraph blocks(graph_,
917 GetAllocator(),
918 "entry",
919 "exit",
920 { { "entry", "loop_pre_header" },
921 { "loop_pre_header", "loop_entry" },
922 { "loop_entry", "loop_body" },
923 { "loop_entry", "loop_post" },
924 { "loop_body", "loop_entry" },
925 { "loop_post", "exit" } });
926#define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
927 GET_BLOCK(entry);
928 GET_BLOCK(loop_pre_header);
929 GET_BLOCK(loop_entry);
930 GET_BLOCK(loop_body);
931 GET_BLOCK(loop_post);
932 GET_BLOCK(exit);
933#undef GET_BLOCK
934
935 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
936 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
937 HInstruction* eighty_const = graph_->GetConstant(DataType::Type::kInt32, 80);
938 HInstruction* entry_goto = new (GetAllocator()) HGoto();
939 entry->AddInstruction(entry_goto);
940
941 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, eighty_const, 0, 0);
942 HInstruction* pre_header_goto = new (GetAllocator()) HGoto();
943 loop_pre_header->AddInstruction(alloc_w);
944 loop_pre_header->AddInstruction(pre_header_goto);
945 // environment
946 ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction));
947 ManuallyBuildEnvFor(alloc_w, &alloc_locals);
948
949 // loop-start
950 HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
951 HPhi* t_phi = new (GetAllocator()) HPhi(GetAllocator(), 1, 0, DataType::Type::kInt32);
952 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
953 HInstruction* i_cmp_top = new (GetAllocator()) HGreaterThanOrEqual(i_phi, eighty_const);
954 HInstruction* loop_start_branch = new (GetAllocator()) HIf(i_cmp_top);
955 loop_entry->AddPhi(i_phi);
956 loop_entry->AddPhi(t_phi);
957 loop_entry->AddInstruction(suspend);
958 loop_entry->AddInstruction(i_cmp_top);
959 loop_entry->AddInstruction(loop_start_branch);
960 CHECK_EQ(loop_entry->GetSuccessors().size(), 2u);
961 if (loop_entry->GetNormalSuccessors()[1] != loop_body) {
962 loop_entry->SwapSuccessors();
963 }
964 CHECK_EQ(loop_entry->GetPredecessors().size(), 2u);
965 if (loop_entry->GetPredecessors()[0] != loop_pre_header) {
966 loop_entry->SwapPredecessors();
967 }
968 i_phi->AddInput(one_const);
969 t_phi->AddInput(zero_const);
970
971 // environment
972 ArenaVector<HInstruction*> suspend_locals({ alloc_w, i_phi, t_phi },
973 GetAllocator()->Adapter(kArenaAllocInstruction));
974 ManuallyBuildEnvFor(suspend, &suspend_locals);
975
976 // BODY
977 HInstruction* last_i = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, one_const);
978 HInstruction* last_get =
979 new (GetAllocator()) HArrayGet(alloc_w, last_i, DataType::Type::kInt32, 0);
980 HInvoke* body_value = new (GetAllocator())
981 HInvokeStaticOrDirect(GetAllocator(),
982 2,
983 DataType::Type::kInt32,
984 0,
985 { nullptr, 0 },
986 nullptr,
987 {},
988 InvokeType::kStatic,
989 { nullptr, 0 },
990 HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
991 body_value->SetRawInputAt(0, last_get);
992 body_value->SetRawInputAt(1, one_const);
993 HInstruction* body_set =
994 new (GetAllocator()) HArraySet(alloc_w, i_phi, body_value, DataType::Type::kInt32, 0);
995 HInstruction* body_get =
996 new (GetAllocator()) HArrayGet(alloc_w, i_phi, DataType::Type::kInt32, 0);
997 HInvoke* t_next = new (GetAllocator())
998 HInvokeStaticOrDirect(GetAllocator(),
999 2,
1000 DataType::Type::kInt32,
1001 0,
1002 { nullptr, 0 },
1003 nullptr,
1004 {},
1005 InvokeType::kStatic,
1006 { nullptr, 0 },
1007 HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
1008 t_next->SetRawInputAt(0, body_get);
1009 t_next->SetRawInputAt(1, t_phi);
1010 HInstruction* i_next = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_phi, one_const);
1011 HInstruction* body_goto = new (GetAllocator()) HGoto();
1012 loop_body->AddInstruction(last_i);
1013 loop_body->AddInstruction(last_get);
1014 loop_body->AddInstruction(body_value);
1015 loop_body->AddInstruction(body_set);
1016 loop_body->AddInstruction(body_get);
1017 loop_body->AddInstruction(t_next);
1018 loop_body->AddInstruction(i_next);
1019 loop_body->AddInstruction(body_goto);
1020 body_value->CopyEnvironmentFrom(suspend->GetEnvironment());
1021
1022 i_phi->AddInput(i_next);
1023 t_phi->AddInput(t_next);
1024 t_next->CopyEnvironmentFrom(suspend->GetEnvironment());
1025
1026 // loop-post
1027 HInstruction* return_inst = new (GetAllocator()) HReturn(t_phi);
1028 loop_post->AddInstruction(return_inst);
1029
1030 // exit
1031 HInstruction* exit_inst = new (GetAllocator()) HExit();
1032 exit->AddInstruction(exit_inst);
1033
1034 graph_->ClearDominanceInformation();
1035 graph_->ClearLoopInformation();
1036 PerformLSE();
1037
1038 // TODO Technically this is optimizable. LSE just needs to add phis to keep
1039 // track of the last `N` values set where `N` is how many locations we can go
1040 // back into the array.
1041 if (IsRemoved(last_get)) {
1042 // If we were able to remove the previous read the entire array should be removable.
1043 EXPECT_TRUE(IsRemoved(body_set));
1044 EXPECT_TRUE(IsRemoved(alloc_w));
1045 } else {
1046 // This is the branch we actually take for now. If we rely on being able to
1047 // read the array we'd better remember to write to it as well.
1048 EXPECT_FALSE(IsRemoved(body_set));
1049 }
1050 // The last 'get' should always be removable.
1051 EXPECT_TRUE(IsRemoved(body_get));
1052}
1053
1054
1055// void DO_CAL2() {
1056// int i = 1;
1057// int[] w = new int[80];
1058// int t = 0;
1059// while (i < 80) {
1060// w[i] = PLEASE_INTERLEAVE(w[i - 1], 1) // <-- removed
1061// t = PLEASE_SELECT(w[i], t);
1062// w[i] = PLEASE_INTERLEAVE(w[i - 1], 1) // <-- removed
1063// t = PLEASE_SELECT(w[i], t);
1064// w[i] = PLEASE_INTERLEAVE(w[i - 1], 1) // <-- kept
1065// t = PLEASE_SELECT(w[i], t);
1066// i++;
1067// }
1068// return t;
1069// }
1070TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) {
1071 CreateGraph();
1072 AdjacencyListGraph blocks(graph_,
1073 GetAllocator(),
1074 "entry",
1075 "exit",
1076 { { "entry", "loop_pre_header" },
1077 { "loop_pre_header", "loop_entry" },
1078 { "loop_entry", "loop_body" },
1079 { "loop_entry", "loop_post" },
1080 { "loop_body", "loop_entry" },
1081 { "loop_post", "exit" } });
1082#define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1083 GET_BLOCK(entry);
1084 GET_BLOCK(loop_pre_header);
1085 GET_BLOCK(loop_entry);
1086 GET_BLOCK(loop_body);
1087 GET_BLOCK(loop_post);
1088 GET_BLOCK(exit);
1089#undef GET_BLOCK
1090
1091 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1092 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1093 HInstruction* eighty_const = graph_->GetConstant(DataType::Type::kInt32, 80);
1094 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1095 entry->AddInstruction(entry_goto);
1096
1097 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, eighty_const, 0, 0);
1098 HInstruction* pre_header_goto = new (GetAllocator()) HGoto();
1099 loop_pre_header->AddInstruction(alloc_w);
1100 loop_pre_header->AddInstruction(pre_header_goto);
1101 // environment
1102 ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction));
1103 ManuallyBuildEnvFor(alloc_w, &alloc_locals);
1104
1105 // loop-start
1106 HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
1107 HPhi* t_phi = new (GetAllocator()) HPhi(GetAllocator(), 1, 0, DataType::Type::kInt32);
1108 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
1109 HInstruction* i_cmp_top = new (GetAllocator()) HGreaterThanOrEqual(i_phi, eighty_const);
1110 HInstruction* loop_start_branch = new (GetAllocator()) HIf(i_cmp_top);
1111 loop_entry->AddPhi(i_phi);
1112 loop_entry->AddPhi(t_phi);
1113 loop_entry->AddInstruction(suspend);
1114 loop_entry->AddInstruction(i_cmp_top);
1115 loop_entry->AddInstruction(loop_start_branch);
1116 CHECK_EQ(loop_entry->GetSuccessors().size(), 2u);
1117 if (loop_entry->GetNormalSuccessors()[1] != loop_body) {
1118 loop_entry->SwapSuccessors();
1119 }
1120 CHECK_EQ(loop_entry->GetPredecessors().size(), 2u);
1121 if (loop_entry->GetPredecessors()[0] != loop_pre_header) {
1122 loop_entry->SwapPredecessors();
1123 }
1124 i_phi->AddInput(one_const);
1125 t_phi->AddInput(zero_const);
1126
1127 // environment
1128 ArenaVector<HInstruction*> suspend_locals({ alloc_w, i_phi, t_phi },
1129 GetAllocator()->Adapter(kArenaAllocInstruction));
1130 ManuallyBuildEnvFor(suspend, &suspend_locals);
1131
1132 // BODY
1133 HInstruction* last_i = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, one_const);
1134 HInstruction* last_get_1, *last_get_2, *last_get_3;
1135 HInstruction* body_value_1, *body_value_2, *body_value_3;
1136 HInstruction* body_set_1, *body_set_2, *body_set_3;
1137 HInstruction* body_get_1, *body_get_2, *body_get_3;
1138 HInstruction* t_next_1, *t_next_2, *t_next_3;
1139 auto make_instructions = [&](HInstruction* last_t_value) {
1140 HInstruction* last_get =
1141 new (GetAllocator()) HArrayGet(alloc_w, last_i, DataType::Type::kInt32, 0);
1142 HInvoke* body_value = new (GetAllocator())
1143 HInvokeStaticOrDirect(GetAllocator(),
1144 2,
1145 DataType::Type::kInt32,
1146 0,
1147 { nullptr, 0 },
1148 nullptr,
1149 {},
1150 InvokeType::kStatic,
1151 { nullptr, 0 },
1152 HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
1153 body_value->SetRawInputAt(0, last_get);
1154 body_value->SetRawInputAt(1, one_const);
1155 HInstruction* body_set =
1156 new (GetAllocator()) HArraySet(alloc_w, i_phi, body_value, DataType::Type::kInt32, 0);
1157 HInstruction* body_get =
1158 new (GetAllocator()) HArrayGet(alloc_w, i_phi, DataType::Type::kInt32, 0);
1159 HInvoke* t_next = new (GetAllocator())
1160 HInvokeStaticOrDirect(GetAllocator(),
1161 2,
1162 DataType::Type::kInt32,
1163 0,
1164 { nullptr, 0 },
1165 nullptr,
1166 {},
1167 InvokeType::kStatic,
1168 { nullptr, 0 },
1169 HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
1170 t_next->SetRawInputAt(0, body_get);
1171 t_next->SetRawInputAt(1, last_t_value);
1172 loop_body->AddInstruction(last_get);
1173 loop_body->AddInstruction(body_value);
1174 loop_body->AddInstruction(body_set);
1175 loop_body->AddInstruction(body_get);
1176 loop_body->AddInstruction(t_next);
1177 return std::make_tuple(last_get, body_value, body_set, body_get, t_next);
1178 };
1179 std::tie(last_get_1, body_value_1, body_set_1, body_get_1, t_next_1) = make_instructions(t_phi);
1180 std::tie(last_get_2, body_value_2, body_set_2, body_get_2, t_next_2) =
1181 make_instructions(t_next_1);
1182 std::tie(last_get_3, body_value_3, body_set_3, body_get_3, t_next_3) =
1183 make_instructions(t_next_2);
1184 HInstruction* i_next = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_phi, one_const);
1185 HInstruction* body_goto = new (GetAllocator()) HGoto();
1186 loop_body->InsertInstructionBefore(last_i, last_get_1);
1187 loop_body->AddInstruction(i_next);
1188 loop_body->AddInstruction(body_goto);
1189 body_value_1->CopyEnvironmentFrom(suspend->GetEnvironment());
1190 body_value_2->CopyEnvironmentFrom(suspend->GetEnvironment());
1191 body_value_3->CopyEnvironmentFrom(suspend->GetEnvironment());
1192
1193 i_phi->AddInput(i_next);
1194 t_phi->AddInput(t_next_3);
1195 t_next_1->CopyEnvironmentFrom(suspend->GetEnvironment());
1196 t_next_2->CopyEnvironmentFrom(suspend->GetEnvironment());
1197 t_next_3->CopyEnvironmentFrom(suspend->GetEnvironment());
1198
1199 // loop-post
1200 HInstruction* return_inst = new (GetAllocator()) HReturn(t_phi);
1201 loop_post->AddInstruction(return_inst);
1202
1203 // exit
1204 HInstruction* exit_inst = new (GetAllocator()) HExit();
1205 exit->AddInstruction(exit_inst);
1206
1207 graph_->ClearDominanceInformation();
1208 graph_->ClearLoopInformation();
1209 PerformLSE();
1210
1211 // TODO Technically this is optimizable. LSE just needs to add phis to keep
1212 // track of the last `N` values set where `N` is how many locations we can go
1213 // back into the array.
1214 if (IsRemoved(last_get_1)) {
1215 // If we were able to remove the previous read the entire array should be removable.
1216 EXPECT_TRUE(IsRemoved(body_set_1));
1217 EXPECT_TRUE(IsRemoved(body_set_2));
1218 EXPECT_TRUE(IsRemoved(body_set_3));
1219 EXPECT_TRUE(IsRemoved(last_get_1));
1220 EXPECT_TRUE(IsRemoved(last_get_2));
1221 EXPECT_TRUE(IsRemoved(alloc_w));
1222 } else {
1223 // This is the branch we actually take for now. If we rely on being able to
1224 // read the array we'd better remember to write to it as well.
1225 EXPECT_FALSE(IsRemoved(body_set_3));
1226 }
1227 // The last 'get' should always be removable.
1228 EXPECT_TRUE(IsRemoved(body_get_1));
1229 EXPECT_TRUE(IsRemoved(body_get_2));
1230 EXPECT_TRUE(IsRemoved(body_get_3));
1231 // shadowed writes should always be removed
1232 EXPECT_TRUE(IsRemoved(body_set_1));
1233 EXPECT_TRUE(IsRemoved(body_set_2));
1234}
1235
1236TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) {
1237 CreateGraph();
1238 AdjacencyListGraph blocks(graph_,
1239 GetAllocator(),
1240 "entry",
1241 "exit",
1242 { { "entry", "start" },
1243 { "start", "left" },
1244 { "start", "right" },
1245 { "left", "ret" },
1246 { "right", "ret" },
1247 { "ret", "exit" } });
1248#define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1249 GET_BLOCK(entry);
1250 GET_BLOCK(start);
1251 GET_BLOCK(left);
1252 GET_BLOCK(right);
1253 GET_BLOCK(ret);
1254 GET_BLOCK(exit);
1255#undef GET_BLOCK
1256
1257 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1258 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1259 HInstruction* two_const = graph_->GetConstant(DataType::Type::kInt32, 2);
1260 HInstruction* param = new (GetAllocator())
1261 HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 0, DataType::Type::kBool);
1262 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1263 entry->AddInstruction(param);
1264 entry->AddInstruction(entry_goto);
1265
1266 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, two_const, 0, 0);
1267 HInstruction* branch = new (GetAllocator()) HIf(param);
1268 start->AddInstruction(alloc_w);
1269 start->AddInstruction(branch);
1270 // environment
1271 ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction));
1272 ManuallyBuildEnvFor(alloc_w, &alloc_locals);
1273
1274 // left
1275 HInvoke* left_value = new (GetAllocator())
1276 HInvokeStaticOrDirect(GetAllocator(),
1277 1,
1278 DataType::Type::kInt32,
1279 0,
1280 { nullptr, 0 },
1281 nullptr,
1282 {},
1283 InvokeType::kStatic,
1284 { nullptr, 0 },
1285 HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
1286 left_value->SetRawInputAt(0, zero_const);
1287 HInstruction* left_set_1 =
1288 new (GetAllocator()) HArraySet(alloc_w, zero_const, left_value, DataType::Type::kInt32, 0);
1289 HInstruction* left_set_2 =
1290 new (GetAllocator()) HArraySet(alloc_w, one_const, zero_const, DataType::Type::kInt32, 0);
1291 HInstruction* left_goto = new (GetAllocator()) HGoto();
1292 left->AddInstruction(left_value);
1293 left->AddInstruction(left_set_1);
1294 left->AddInstruction(left_set_2);
1295 left->AddInstruction(left_goto);
1296 ArenaVector<HInstruction*> left_locals({ alloc_w },
1297 GetAllocator()->Adapter(kArenaAllocInstruction));
1298 ManuallyBuildEnvFor(left_value, &alloc_locals);
1299
1300 // right
1301 HInvoke* right_value = new (GetAllocator())
1302 HInvokeStaticOrDirect(GetAllocator(),
1303 1,
1304 DataType::Type::kInt32,
1305 0,
1306 { nullptr, 0 },
1307 nullptr,
1308 {},
1309 InvokeType::kStatic,
1310 { nullptr, 0 },
1311 HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
1312 right_value->SetRawInputAt(0, one_const);
1313 HInstruction* right_set_1 =
1314 new (GetAllocator()) HArraySet(alloc_w, zero_const, right_value, DataType::Type::kInt32, 0);
1315 HInstruction* right_set_2 =
1316 new (GetAllocator()) HArraySet(alloc_w, one_const, zero_const, DataType::Type::kInt32, 0);
1317 HInstruction* right_goto = new (GetAllocator()) HGoto();
1318 right->AddInstruction(right_value);
1319 right->AddInstruction(right_set_1);
1320 right->AddInstruction(right_set_2);
1321 right->AddInstruction(right_goto);
1322 ArenaVector<HInstruction*> right_locals({ alloc_w },
1323 GetAllocator()->Adapter(kArenaAllocInstruction));
1324 ManuallyBuildEnvFor(right_value, &alloc_locals);
1325
1326 // ret
1327 HInstruction* read_1 =
1328 new (GetAllocator()) HArrayGet(alloc_w, zero_const, DataType::Type::kInt32, 0);
1329 HInstruction* read_2 =
1330 new (GetAllocator()) HArrayGet(alloc_w, one_const, DataType::Type::kInt32, 0);
1331 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, read_1, read_2);
1332 HInstruction* return_inst = new (GetAllocator()) HReturn(add);
1333 ret->AddInstruction(read_1);
1334 ret->AddInstruction(read_2);
1335 ret->AddInstruction(add);
1336 ret->AddInstruction(return_inst);
1337
1338 // exit
1339 HInstruction* exit_inst = new (GetAllocator()) HExit();
1340 exit->AddInstruction(exit_inst);
1341
1342 graph_->ClearDominanceInformation();
1343 graph_->ClearLoopInformation();
1344 PerformLSE();
1345
1346 EXPECT_TRUE(IsRemoved(read_1));
1347 EXPECT_TRUE(IsRemoved(read_2));
1348 EXPECT_TRUE(IsRemoved(left_set_1));
1349 EXPECT_TRUE(IsRemoved(left_set_2));
1350 EXPECT_TRUE(IsRemoved(right_set_1));
1351 EXPECT_TRUE(IsRemoved(right_set_2));
1352 EXPECT_TRUE(IsRemoved(alloc_w));
1353
1354 EXPECT_FALSE(IsRemoved(left_value));
1355 EXPECT_FALSE(IsRemoved(right_value));
1356}
1357
1358TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) {
1359 CreateGraph();
1360 AdjacencyListGraph blocks(graph_,
1361 GetAllocator(),
1362 "entry",
1363 "exit",
1364 { { "entry", "start" },
1365 { "start", "left" },
1366 { "start", "right" },
1367 { "left", "ret" },
1368 { "right", "ret" },
1369 { "ret", "exit" } });
1370#define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1371 GET_BLOCK(entry);
1372 GET_BLOCK(start);
1373 GET_BLOCK(left);
1374 GET_BLOCK(right);
1375 GET_BLOCK(ret);
1376 GET_BLOCK(exit);
1377#undef GET_BLOCK
1378
1379 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1380 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1381 HInstruction* two_const = graph_->GetConstant(DataType::Type::kInt32, 2);
1382 HInstruction* param = new (GetAllocator())
1383 HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 0, DataType::Type::kBool);
1384 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1385 entry->AddInstruction(param);
1386 entry->AddInstruction(entry_goto);
1387
1388 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, two_const, 0, 0);
1389 HInstruction* branch = new (GetAllocator()) HIf(param);
1390 start->AddInstruction(alloc_w);
1391 start->AddInstruction(branch);
1392 // environment
1393 ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction));
1394 ManuallyBuildEnvFor(alloc_w, &alloc_locals);
1395
1396 // left
1397 HInstruction* left_set_1 =
1398 new (GetAllocator()) HArraySet(alloc_w, zero_const, one_const, DataType::Type::kInt32, 0);
1399 HInstruction* left_set_2 =
1400 new (GetAllocator()) HArraySet(alloc_w, zero_const, zero_const, DataType::Type::kInt32, 0);
1401 HInstruction* left_goto = new (GetAllocator()) HGoto();
1402 left->AddInstruction(left_set_1);
1403 left->AddInstruction(left_set_2);
1404 left->AddInstruction(left_goto);
1405
1406 // right
1407 HInstruction* right_set_1 =
1408 new (GetAllocator()) HArraySet(alloc_w, one_const, one_const, DataType::Type::kInt32, 0);
1409 HInstruction* right_set_2 =
1410 new (GetAllocator()) HArraySet(alloc_w, one_const, zero_const, DataType::Type::kInt32, 0);
1411 HInstruction* right_goto = new (GetAllocator()) HGoto();
1412 right->AddInstruction(right_set_1);
1413 right->AddInstruction(right_set_2);
1414 right->AddInstruction(right_goto);
1415
1416 // ret
1417 HInstruction* read_1 =
1418 new (GetAllocator()) HArrayGet(alloc_w, zero_const, DataType::Type::kInt32, 0);
1419 HInstruction* read_2 =
1420 new (GetAllocator()) HArrayGet(alloc_w, one_const, DataType::Type::kInt32, 0);
1421 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, read_1, read_2);
1422 HInstruction* return_inst = new (GetAllocator()) HReturn(add);
1423 ret->AddInstruction(read_1);
1424 ret->AddInstruction(read_2);
1425 ret->AddInstruction(add);
1426 ret->AddInstruction(return_inst);
1427
1428 // exit
1429 HInstruction* exit_inst = new (GetAllocator()) HExit();
1430 exit->AddInstruction(exit_inst);
1431
1432 graph_->ClearDominanceInformation();
1433 graph_->ClearLoopInformation();
1434 PerformLSE();
1435
1436 EXPECT_TRUE(IsRemoved(read_1));
1437 EXPECT_TRUE(IsRemoved(read_2));
1438 EXPECT_TRUE(IsRemoved(left_set_1));
1439 EXPECT_TRUE(IsRemoved(left_set_2));
1440 EXPECT_TRUE(IsRemoved(right_set_1));
1441 EXPECT_TRUE(IsRemoved(right_set_2));
1442 EXPECT_TRUE(IsRemoved(alloc_w));
1443}
1444
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001445} // namespace art