1 /*
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 "load_store_elimination.h"
18
19 #include <initializer_list>
20 #include <memory>
21 #include <tuple>
22 #include <variant>
23
24 #include "base/iteration_range.h"
25 #include "compilation_kind.h"
26 #include "dex/dex_file_types.h"
27 #include "entrypoints/quick/quick_entrypoints.h"
28 #include "entrypoints/quick/quick_entrypoints_enum.h"
29 #include "gtest/gtest.h"
30 #include "handle_scope.h"
31 #include "load_store_analysis.h"
32 #include "nodes.h"
33 #include "optimizing/data_type.h"
34 #include "optimizing/instruction_simplifier.h"
35 #include "optimizing/optimizing_compiler_stats.h"
36 #include "optimizing_unit_test.h"
37 #include "scoped_thread_state_change.h"
38
39 namespace art {
40
41 #define CHECK_SUBROUTINE_FAILURE() \
42 do { \
43 if (HasFatalFailure()) { \
44 return; \
45 } \
46 } while (false)
47
48 template <typename SuperTest>
49 class LoadStoreEliminationTestBase : public SuperTest, public OptimizingUnitTestHelper {
50 public:
SetUp()51 void SetUp() override {
52 SuperTest::SetUp();
53 gLogVerbosity.compiler = true;
54 }
55
TearDown()56 void TearDown() override {
57 SuperTest::TearDown();
58 gLogVerbosity.compiler = false;
59 }
60
PerformLSE(bool with_partial=true)61 void PerformLSE(bool with_partial = true) {
62 graph_->BuildDominatorTree();
63 LoadStoreElimination lse(graph_, /*stats=*/nullptr);
64 lse.Run(with_partial);
65 std::ostringstream oss;
66 EXPECT_TRUE(CheckGraphSkipRefTypeInfoChecks(oss)) << oss.str();
67 }
68
PerformLSEWithPartial()69 void PerformLSEWithPartial() {
70 PerformLSE(true);
71 }
72
PerformLSENoPartial()73 void PerformLSENoPartial() {
74 PerformLSE(false);
75 }
76
77 // Create instructions shared among tests.
CreateEntryBlockInstructions()78 void CreateEntryBlockInstructions() {
79 HInstruction* c1 = graph_->GetIntConstant(1);
80 HInstruction* c4 = graph_->GetIntConstant(4);
81 i_add1_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c1);
82 i_add4_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c4);
83 entry_block_->AddInstruction(i_add1_);
84 entry_block_->AddInstruction(i_add4_);
85 entry_block_->AddInstruction(new (GetAllocator()) HGoto());
86 }
87
88 // Create the major CFG used by tests:
89 // entry
90 // |
91 // pre_header
92 // |
93 // loop[]
94 // |
95 // return
96 // |
97 // exit
CreateTestControlFlowGraph()98 void CreateTestControlFlowGraph() {
99 InitGraphAndParameters();
100 pre_header_ = AddNewBlock();
101 loop_ = AddNewBlock();
102
103 entry_block_->ReplaceSuccessor(return_block_, pre_header_);
104 pre_header_->AddSuccessor(loop_);
105 loop_->AddSuccessor(loop_);
106 loop_->AddSuccessor(return_block_);
107
108 HInstruction* c0 = graph_->GetIntConstant(0);
109 HInstruction* c1 = graph_->GetIntConstant(1);
110 HInstruction* c128 = graph_->GetIntConstant(128);
111
112 CreateEntryBlockInstructions();
113
114 // pre_header block
115 // phi = 0;
116 phi_ = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
117 loop_->AddPhi(phi_);
118 pre_header_->AddInstruction(new (GetAllocator()) HGoto());
119 phi_->AddInput(c0);
120
121 // loop block:
122 // suspend_check
123 // phi++;
124 // if (phi >= 128)
125 suspend_check_ = new (GetAllocator()) HSuspendCheck();
126 HInstruction* inc_phi = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi_, c1);
127 HInstruction* cmp = new (GetAllocator()) HGreaterThanOrEqual(phi_, c128);
128 HInstruction* hif = new (GetAllocator()) HIf(cmp);
129 loop_->AddInstruction(suspend_check_);
130 loop_->AddInstruction(inc_phi);
131 loop_->AddInstruction(cmp);
132 loop_->AddInstruction(hif);
133 phi_->AddInput(inc_phi);
134
135 CreateEnvForSuspendCheck();
136 }
137
CreateEnvForSuspendCheck()138 void CreateEnvForSuspendCheck() {
139 ManuallyBuildEnvFor(suspend_check_, {array_, i_, j_});
140 }
141
142 // Create the diamond-shaped CFG:
143 // upper
144 // / \
145 // left right
146 // \ /
147 // down
148 //
149 // Return: the basic blocks forming the CFG in the following order {upper, left, right, down}.
CreateDiamondShapedCFG()150 std::tuple<HBasicBlock*, HBasicBlock*, HBasicBlock*, HBasicBlock*> CreateDiamondShapedCFG() {
151 InitGraphAndParameters();
152 CreateEntryBlockInstructions();
153
154 HBasicBlock* upper = AddNewBlock();
155 HBasicBlock* left = AddNewBlock();
156 HBasicBlock* right = AddNewBlock();
157
158 entry_block_->ReplaceSuccessor(return_block_, upper);
159 upper->AddSuccessor(left);
160 upper->AddSuccessor(right);
161 left->AddSuccessor(return_block_);
162 right->AddSuccessor(return_block_);
163
164 HInstruction* cmp = new (GetAllocator()) HGreaterThanOrEqual(i_, j_);
165 HInstruction* hif = new (GetAllocator()) HIf(cmp);
166 upper->AddInstruction(cmp);
167 upper->AddInstruction(hif);
168
169 left->AddInstruction(new (GetAllocator()) HGoto());
170 right->AddInstruction(new (GetAllocator()) HGoto());
171
172 return std::make_tuple(upper, left, right, return_block_);
173 }
174
175 // Add a HVecLoad instruction to the end of the provided basic block.
176 //
177 // Return: the created HVecLoad instruction.
AddVecLoad(HBasicBlock * block,HInstruction * array,HInstruction * index)178 HInstruction* AddVecLoad(HBasicBlock* block, HInstruction* array, HInstruction* index) {
179 DCHECK(block != nullptr);
180 DCHECK(array != nullptr);
181 DCHECK(index != nullptr);
182 HInstruction* vload =
183 new (GetAllocator()) HVecLoad(GetAllocator(),
184 array,
185 index,
186 DataType::Type::kInt32,
187 SideEffects::ArrayReadOfType(DataType::Type::kInt32),
188 4,
189 /*is_string_char_at*/ false,
190 kNoDexPc);
191 block->InsertInstructionBefore(vload, block->GetLastInstruction());
192 return vload;
193 }
194
195 // Add a HVecStore instruction to the end of the provided basic block.
196 // If no vdata is specified, generate HVecStore: array[index] = [1,1,1,1].
197 //
198 // Return: the created HVecStore instruction.
AddVecStore(HBasicBlock * block,HInstruction * array,HInstruction * index,HInstruction * vdata=nullptr)199 HInstruction* AddVecStore(HBasicBlock* block,
200 HInstruction* array,
201 HInstruction* index,
202 HInstruction* vdata = nullptr) {
203 DCHECK(block != nullptr);
204 DCHECK(array != nullptr);
205 DCHECK(index != nullptr);
206 if (vdata == nullptr) {
207 HInstruction* c1 = graph_->GetIntConstant(1);
208 vdata = new (GetAllocator())
209 HVecReplicateScalar(GetAllocator(), c1, DataType::Type::kInt32, 4, kNoDexPc);
210 block->InsertInstructionBefore(vdata, block->GetLastInstruction());
211 }
212 HInstruction* vstore =
213 new (GetAllocator()) HVecStore(GetAllocator(),
214 array,
215 index,
216 vdata,
217 DataType::Type::kInt32,
218 SideEffects::ArrayWriteOfType(DataType::Type::kInt32),
219 4,
220 kNoDexPc);
221 block->InsertInstructionBefore(vstore, block->GetLastInstruction());
222 return vstore;
223 }
224
225 // Add a HArrayGet instruction to the end of the provided basic block.
226 //
227 // Return: the created HArrayGet instruction.
AddArrayGet(HBasicBlock * block,HInstruction * array,HInstruction * index)228 HInstruction* AddArrayGet(HBasicBlock* block, HInstruction* array, HInstruction* index) {
229 DCHECK(block != nullptr);
230 DCHECK(array != nullptr);
231 DCHECK(index != nullptr);
232 HInstruction* get = new (GetAllocator()) HArrayGet(array, index, DataType::Type::kInt32, 0);
233 block->InsertInstructionBefore(get, block->GetLastInstruction());
234 return get;
235 }
236
237 // Add a HArraySet instruction to the end of the provided basic block.
238 // If no data is specified, generate HArraySet: array[index] = 1.
239 //
240 // Return: the created HArraySet instruction.
AddArraySet(HBasicBlock * block,HInstruction * array,HInstruction * index,HInstruction * data=nullptr)241 HInstruction* AddArraySet(HBasicBlock* block,
242 HInstruction* array,
243 HInstruction* index,
244 HInstruction* data = nullptr) {
245 DCHECK(block != nullptr);
246 DCHECK(array != nullptr);
247 DCHECK(index != nullptr);
248 if (data == nullptr) {
249 data = graph_->GetIntConstant(1);
250 }
251 HInstruction* store =
252 new (GetAllocator()) HArraySet(array, index, data, DataType::Type::kInt32, 0);
253 block->InsertInstructionBefore(store, block->GetLastInstruction());
254 return store;
255 }
256
InitGraphAndParameters()257 void InitGraphAndParameters() {
258 InitGraph();
259 AddParameter(new (GetAllocator()) HParameterValue(
260 graph_->GetDexFile(), dex::TypeIndex(0), 0, DataType::Type::kInt32));
261 array_ = parameters_.back();
262 AddParameter(new (GetAllocator()) HParameterValue(
263 graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kInt32));
264 i_ = parameters_.back();
265 AddParameter(new (GetAllocator()) HParameterValue(
266 graph_->GetDexFile(), dex::TypeIndex(1), 2, DataType::Type::kInt32));
267 j_ = parameters_.back();
268 }
269
270 HBasicBlock* pre_header_;
271 HBasicBlock* loop_;
272
273 HInstruction* array_;
274 HInstruction* i_;
275 HInstruction* j_;
276 HInstruction* i_add1_;
277 HInstruction* i_add4_;
278 HInstruction* suspend_check_;
279
280 HPhi* phi_;
281 };
282
283 class LoadStoreEliminationTest : public LoadStoreEliminationTestBase<CommonCompilerTest> {};
284
285 enum class TestOrder { kSameAsAlloc, kReverseOfAlloc };
operator <<(std::ostream & os,const TestOrder & ord)286 std::ostream& operator<<(std::ostream& os, const TestOrder& ord) {
287 switch (ord) {
288 case TestOrder::kSameAsAlloc:
289 return os << "SameAsAlloc";
290 case TestOrder::kReverseOfAlloc:
291 return os << "ReverseOfAlloc";
292 }
293 }
294
295 class OrderDependentTestGroup
296 : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<TestOrder>> {};
297
298 // Various configs we can use for testing. Currently used in PartialComparison tests.
299 struct PartialComparisonKind {
300 public:
301 enum class Type : uint8_t { kEquals, kNotEquals };
302 enum class Target : uint8_t { kNull, kValue, kSelf };
303 enum class Position : uint8_t { kLeft, kRight };
304
305 const Type type_;
306 const Target target_;
307 const Position position_;
308
IsDefinitelyFalseart::PartialComparisonKind309 bool IsDefinitelyFalse() const {
310 return !IsPossiblyTrue();
311 }
IsPossiblyFalseart::PartialComparisonKind312 bool IsPossiblyFalse() const {
313 return !IsDefinitelyTrue();
314 }
IsDefinitelyTrueart::PartialComparisonKind315 bool IsDefinitelyTrue() const {
316 if (target_ == Target::kSelf) {
317 return type_ == Type::kEquals;
318 } else if (target_ == Target::kNull) {
319 return type_ == Type::kNotEquals;
320 } else {
321 return false;
322 }
323 }
IsPossiblyTrueart::PartialComparisonKind324 bool IsPossiblyTrue() const {
325 if (target_ == Target::kSelf) {
326 return type_ == Type::kEquals;
327 } else if (target_ == Target::kNull) {
328 return type_ == Type::kNotEquals;
329 } else {
330 return true;
331 }
332 }
Dumpart::PartialComparisonKind333 std::ostream& Dump(std::ostream& os) const {
334 os << "PartialComparisonKind{" << (type_ == Type::kEquals ? "kEquals" : "kNotEquals") << ", "
335 << (target_ == Target::kNull ? "kNull" : (target_ == Target::kSelf ? "kSelf" : "kValue"))
336 << ", " << (position_ == Position::kLeft ? "kLeft" : "kRight") << "}";
337 return os;
338 }
339 };
340
operator <<(std::ostream & os,const PartialComparisonKind & comp)341 std::ostream& operator<<(std::ostream& os, const PartialComparisonKind& comp) {
342 return comp.Dump(os);
343 }
344
345 class PartialComparisonTestGroup
346 : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<PartialComparisonKind>> {
347 public:
348 enum class ComparisonPlacement {
349 kBeforeEscape,
350 kInEscape,
351 kAfterEscape,
352 };
CheckFinalInstruction(HInstruction * ins,ComparisonPlacement placement)353 void CheckFinalInstruction(HInstruction* ins, ComparisonPlacement placement) {
354 using Target = PartialComparisonKind::Target;
355 using Type = PartialComparisonKind::Type;
356 using Position = PartialComparisonKind::Position;
357 PartialComparisonKind kind = GetParam();
358 if (ins->IsIntConstant()) {
359 if (kind.IsDefinitelyTrue()) {
360 EXPECT_TRUE(ins->AsIntConstant()->IsTrue()) << kind << " " << *ins;
361 } else if (kind.IsDefinitelyFalse()) {
362 EXPECT_TRUE(ins->AsIntConstant()->IsFalse()) << kind << " " << *ins;
363 } else {
364 EXPECT_EQ(placement, ComparisonPlacement::kBeforeEscape);
365 EXPECT_EQ(kind.target_, Target::kValue);
366 // We are before escape so value is not the object
367 if (kind.type_ == Type::kEquals) {
368 EXPECT_TRUE(ins->AsIntConstant()->IsFalse()) << kind << " " << *ins;
369 } else {
370 EXPECT_TRUE(ins->AsIntConstant()->IsTrue()) << kind << " " << *ins;
371 }
372 }
373 return;
374 }
375 EXPECT_NE(placement, ComparisonPlacement::kBeforeEscape)
376 << "For comparisons before escape we should always be able to transform into a constant."
377 << " Instead we got:" << std::endl << ins->DumpWithArgs();
378 if (placement == ComparisonPlacement::kInEscape) {
379 // Should be the same type.
380 ASSERT_TRUE(ins->IsEqual() || ins->IsNotEqual()) << *ins;
381 HInstruction* other = kind.position_ == Position::kLeft ? ins->AsBinaryOperation()->GetRight()
382 : ins->AsBinaryOperation()->GetLeft();
383 if (kind.target_ == Target::kSelf) {
384 EXPECT_INS_EQ(ins->AsBinaryOperation()->GetLeft(), ins->AsBinaryOperation()->GetRight())
385 << " ins is: " << *ins;
386 } else if (kind.target_ == Target::kNull) {
387 EXPECT_INS_EQ(other, graph_->GetNullConstant()) << " ins is: " << *ins;
388 } else {
389 EXPECT_TRUE(other->IsStaticFieldGet()) << " ins is: " << *ins;
390 }
391 if (kind.type_ == Type::kEquals) {
392 EXPECT_TRUE(ins->IsEqual()) << *ins;
393 } else {
394 EXPECT_TRUE(ins->IsNotEqual()) << *ins;
395 }
396 } else {
397 ASSERT_EQ(placement, ComparisonPlacement::kAfterEscape);
398 if (kind.type_ == Type::kEquals) {
399 // obj == <anything> can only be true if (1) it's obj == obj or (2) obj has escaped.
400 ASSERT_TRUE(ins->IsAnd()) << ins->DumpWithArgs();
401 EXPECT_TRUE(ins->InputAt(1)->IsEqual()) << ins->DumpWithArgs();
402 } else {
403 // obj != <anything> is true if (2) obj has escaped.
404 ASSERT_TRUE(ins->IsOr()) << ins->DumpWithArgs();
405 EXPECT_TRUE(ins->InputAt(1)->IsNotEqual()) << ins->DumpWithArgs();
406 }
407 // Check the first part of AND is the obj-has-escaped
408 ASSERT_TRUE(ins->InputAt(0)->IsNotEqual()) << ins->DumpWithArgs();
409 EXPECT_TRUE(ins->InputAt(0)->InputAt(0)->IsPhi()) << ins->DumpWithArgs();
410 EXPECT_TRUE(ins->InputAt(0)->InputAt(1)->IsNullConstant()) << ins->DumpWithArgs();
411 // Check the second part of AND is the eq other
412 EXPECT_INS_EQ(ins->InputAt(1)->InputAt(kind.position_ == Position::kLeft ? 0 : 1),
413 ins->InputAt(0)->InputAt(0))
414 << ins->DumpWithArgs();
415 }
416 }
417
418 struct ComparisonInstructions {
AddSetupart::PartialComparisonTestGroup::ComparisonInstructions419 void AddSetup(HBasicBlock* blk) const {
420 for (HInstruction* i : setup_instructions_) {
421 blk->AddInstruction(i);
422 }
423 }
424
AddEnvironmentart::PartialComparisonTestGroup::ComparisonInstructions425 void AddEnvironment(HEnvironment* env) const {
426 for (HInstruction* i : setup_instructions_) {
427 if (i->NeedsEnvironment()) {
428 i->CopyEnvironmentFrom(env);
429 }
430 }
431 }
432
433 const std::vector<HInstruction*> setup_instructions_;
434 HInstruction* const cmp_;
435 };
436
GetComparisonInstructions(HInstruction * partial)437 ComparisonInstructions GetComparisonInstructions(HInstruction* partial) {
438 PartialComparisonKind kind = GetParam();
439 std::vector<HInstruction*> setup;
440 HInstruction* target_other;
441 switch (kind.target_) {
442 case PartialComparisonKind::Target::kSelf:
443 target_other = partial;
444 break;
445 case PartialComparisonKind::Target::kNull:
446 target_other = graph_->GetNullConstant();
447 break;
448 case PartialComparisonKind::Target::kValue: {
449 HInstruction* cls = MakeClassLoad();
450 HInstruction* static_read =
451 new (GetAllocator()) HStaticFieldGet(cls,
452 /* field= */ nullptr,
453 DataType::Type::kReference,
454 /* field_offset= */ MemberOffset(40),
455 /* is_volatile= */ false,
456 /* field_idx= */ 0,
457 /* declaring_class_def_index= */ 0,
458 graph_->GetDexFile(),
459 /* dex_pc= */ 0);
460 setup.push_back(cls);
461 setup.push_back(static_read);
462 target_other = static_read;
463 break;
464 }
465 }
466 HInstruction* target_left;
467 HInstruction* target_right;
468 std::tie(target_left, target_right) = kind.position_ == PartialComparisonKind::Position::kLeft
469 ? std::pair{partial, target_other}
470 : std::pair{target_other, partial};
471 HInstruction* cmp =
472 kind.type_ == PartialComparisonKind::Type::kEquals
473 ? static_cast<HInstruction*>(new (GetAllocator()) HEqual(target_left, target_right))
474 : static_cast<HInstruction*>(new (GetAllocator()) HNotEqual(target_left, target_right));
475 return {setup, cmp};
476 }
477 };
478
TEST_F(LoadStoreEliminationTest,ArrayGetSetElimination)479 TEST_F(LoadStoreEliminationTest, ArrayGetSetElimination) {
480 CreateTestControlFlowGraph();
481
482 HInstruction* c1 = graph_->GetIntConstant(1);
483 HInstruction* c2 = graph_->GetIntConstant(2);
484 HInstruction* c3 = graph_->GetIntConstant(3);
485
486 // array[1] = 1;
487 // x = array[1]; <--- Remove.
488 // y = array[2];
489 // array[1] = 1; <--- Remove, since it stores same value.
490 // array[i] = 3; <--- MAY alias.
491 // array[1] = 1; <--- Cannot remove, even if it stores the same value.
492 AddArraySet(entry_block_, array_, c1, c1);
493 HInstruction* load1 = AddArrayGet(entry_block_, array_, c1);
494 HInstruction* load2 = AddArrayGet(entry_block_, array_, c2);
495 HInstruction* store1 = AddArraySet(entry_block_, array_, c1, c1);
496 AddArraySet(entry_block_, array_, i_, c3);
497 HInstruction* store2 = AddArraySet(entry_block_, array_, c1, c1);
498
499 PerformLSE();
500
501 ASSERT_TRUE(IsRemoved(load1));
502 ASSERT_FALSE(IsRemoved(load2));
503 ASSERT_TRUE(IsRemoved(store1));
504 ASSERT_FALSE(IsRemoved(store2));
505 }
506
TEST_F(LoadStoreEliminationTest,SameHeapValue1)507 TEST_F(LoadStoreEliminationTest, SameHeapValue1) {
508 CreateTestControlFlowGraph();
509
510 HInstruction* c1 = graph_->GetIntConstant(1);
511 HInstruction* c2 = graph_->GetIntConstant(2);
512
513 // Test LSE handling same value stores on array.
514 // array[1] = 1;
515 // array[2] = 1;
516 // array[1] = 1; <--- Can remove.
517 // array[1] = 2; <--- Can NOT remove.
518 AddArraySet(entry_block_, array_, c1, c1);
519 AddArraySet(entry_block_, array_, c2, c1);
520 HInstruction* store1 = AddArraySet(entry_block_, array_, c1, c1);
521 HInstruction* store2 = AddArraySet(entry_block_, array_, c1, c2);
522
523 PerformLSE();
524
525 ASSERT_TRUE(IsRemoved(store1));
526 ASSERT_FALSE(IsRemoved(store2));
527 }
528
TEST_F(LoadStoreEliminationTest,SameHeapValue2)529 TEST_F(LoadStoreEliminationTest, SameHeapValue2) {
530 CreateTestControlFlowGraph();
531
532 // Test LSE handling same value stores on vector.
533 // vdata = [0x1, 0x2, 0x3, 0x4, ...]
534 // VecStore array[i...] = vdata;
535 // VecStore array[j...] = vdata; <--- MAY ALIAS.
536 // VecStore array[i...] = vdata; <--- Cannot Remove, even if it's same value.
537 AddVecStore(entry_block_, array_, i_);
538 AddVecStore(entry_block_, array_, j_);
539 HInstruction* vstore = AddVecStore(entry_block_, array_, i_);
540
541 PerformLSE();
542
543 ASSERT_FALSE(IsRemoved(vstore));
544 }
545
TEST_F(LoadStoreEliminationTest,SameHeapValue3)546 TEST_F(LoadStoreEliminationTest, SameHeapValue3) {
547 CreateTestControlFlowGraph();
548
549 // VecStore array[i...] = vdata;
550 // VecStore array[i+1...] = vdata; <--- MAY alias due to partial overlap.
551 // VecStore array[i...] = vdata; <--- Cannot remove, even if it's same value.
552 AddVecStore(entry_block_, array_, i_);
553 AddVecStore(entry_block_, array_, i_add1_);
554 HInstruction* vstore = AddVecStore(entry_block_, array_, i_);
555
556 PerformLSE();
557
558 ASSERT_FALSE(IsRemoved(vstore));
559 }
560
TEST_F(LoadStoreEliminationTest,OverlappingLoadStore)561 TEST_F(LoadStoreEliminationTest, OverlappingLoadStore) {
562 CreateTestControlFlowGraph();
563
564 HInstruction* c1 = graph_->GetIntConstant(1);
565
566 // Test LSE handling array LSE when there is vector store in between.
567 // a[i] = 1;
568 // .. = a[i]; <-- Remove.
569 // a[i,i+1,i+2,i+3] = data; <-- PARTIAL OVERLAP !
570 // .. = a[i]; <-- Cannot remove.
571 AddArraySet(entry_block_, array_, i_, c1);
572 HInstruction* load1 = AddArrayGet(entry_block_, array_, i_);
573 AddVecStore(entry_block_, array_, i_);
574 HInstruction* load2 = AddArrayGet(entry_block_, array_, i_);
575
576 // Test LSE handling vector load/store partial overlap.
577 // a[i,i+1,i+2,i+3] = data;
578 // a[i+4,i+5,i+6,i+7] = data;
579 // .. = a[i,i+1,i+2,i+3];
580 // .. = a[i+4,i+5,i+6,i+7];
581 // a[i+1,i+2,i+3,i+4] = data; <-- PARTIAL OVERLAP !
582 // .. = a[i,i+1,i+2,i+3];
583 // .. = a[i+4,i+5,i+6,i+7];
584 AddVecStore(entry_block_, array_, i_);
585 AddVecStore(entry_block_, array_, i_add4_);
586 HInstruction* vload1 = AddVecLoad(entry_block_, array_, i_);
587 HInstruction* vload2 = AddVecLoad(entry_block_, array_, i_add4_);
588 AddVecStore(entry_block_, array_, i_add1_);
589 HInstruction* vload3 = AddVecLoad(entry_block_, array_, i_);
590 HInstruction* vload4 = AddVecLoad(entry_block_, array_, i_add4_);
591
592 // Test LSE handling vector LSE when there is array store in between.
593 // a[i,i+1,i+2,i+3] = data;
594 // a[i+1] = 1; <-- PARTIAL OVERLAP !
595 // .. = a[i,i+1,i+2,i+3];
596 AddVecStore(entry_block_, array_, i_);
597 AddArraySet(entry_block_, array_, i_, c1);
598 HInstruction* vload5 = AddVecLoad(entry_block_, array_, i_);
599
600 PerformLSE();
601
602 ASSERT_TRUE(IsRemoved(load1));
603 ASSERT_FALSE(IsRemoved(load2));
604
605 ASSERT_TRUE(IsRemoved(vload1));
606 ASSERT_TRUE(IsRemoved(vload2));
607 ASSERT_FALSE(IsRemoved(vload3));
608 ASSERT_FALSE(IsRemoved(vload4));
609
610 ASSERT_FALSE(IsRemoved(vload5));
611 }
612 // function (int[] a, int j) {
613 // a[j] = 1;
614 // for (int i=0; i<128; i++) {
615 // /* doesn't do any write */
616 // }
617 // a[j] = 1;
TEST_F(LoadStoreEliminationTest,StoreAfterLoopWithoutSideEffects)618 TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithoutSideEffects) {
619 CreateTestControlFlowGraph();
620
621 HInstruction* c1 = graph_->GetIntConstant(1);
622
623 // a[j] = 1
624 AddArraySet(pre_header_, array_, j_, c1);
625
626 // LOOP BODY:
627 // .. = a[i,i+1,i+2,i+3];
628 AddVecLoad(loop_, array_, phi_);
629
630 // a[j] = 1;
631 HInstruction* array_set = AddArraySet(return_block_, array_, j_, c1);
632
633 PerformLSE();
634
635 ASSERT_TRUE(IsRemoved(array_set));
636 }
637
638 // function (int[] a, int j) {
639 // int[] b = new int[128];
640 // a[j] = 0;
641 // for (int phi=0; phi<128; phi++) {
642 // a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
643 // b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
644 // }
645 // a[j] = 0;
646 // }
TEST_F(LoadStoreEliminationTest,StoreAfterSIMDLoopWithSideEffects)647 TEST_F(LoadStoreEliminationTest, StoreAfterSIMDLoopWithSideEffects) {
648 CreateTestControlFlowGraph();
649
650 HInstruction* c0 = graph_->GetIntConstant(0);
651 HInstruction* c128 = graph_->GetIntConstant(128);
652
653 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
654 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
655 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
656
657 // a[j] = 0;
658 AddArraySet(pre_header_, array_, j_, c0);
659
660 // LOOP BODY:
661 // a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
662 // b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
663 AddVecStore(loop_, array_, phi_);
664 HInstruction* vload = AddVecLoad(loop_, array_, phi_);
665 AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
666
667 // a[j] = 0;
668 HInstruction* a_set = AddArraySet(return_block_, array_, j_, c0);
669
670 PerformLSE();
671
672 ASSERT_TRUE(IsRemoved(vload));
673 ASSERT_FALSE(IsRemoved(a_set)); // Cannot remove due to write side-effect in the loop.
674 }
675
676 // function (int[] a, int j) {
677 // int[] b = new int[128];
678 // a[j] = 0;
679 // for (int phi=0; phi<128; phi++) {
680 // a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
681 // b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
682 // }
683 // x = a[j];
684 // }
TEST_F(LoadStoreEliminationTest,LoadAfterSIMDLoopWithSideEffects)685 TEST_F(LoadStoreEliminationTest, LoadAfterSIMDLoopWithSideEffects) {
686 CreateTestControlFlowGraph();
687
688 HInstruction* c0 = graph_->GetIntConstant(0);
689 HInstruction* c128 = graph_->GetIntConstant(128);
690
691 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
692 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
693 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
694
695 // a[j] = 0;
696 AddArraySet(pre_header_, array_, j_, c0);
697
698 // LOOP BODY:
699 // a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
700 // b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
701 AddVecStore(loop_, array_, phi_);
702 HInstruction* vload = AddVecLoad(loop_, array_, phi_);
703 AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
704
705 // x = a[j];
706 HInstruction* load = AddArrayGet(return_block_, array_, j_);
707
708 PerformLSE();
709
710 ASSERT_TRUE(IsRemoved(vload));
711 ASSERT_FALSE(IsRemoved(load)); // Cannot remove due to write side-effect in the loop.
712 }
713
714 // Check that merging works correctly when there are VecStors in predecessors.
715 //
716 // vstore1: a[i,... i + 3] = [1,...1]
717 // / \
718 // / \
719 // vstore2: a[i,... i + 3] = [1,...1] vstore3: a[i+1, ... i + 4] = [1, ... 1]
720 // \ /
721 // \ /
722 // vstore4: a[i,... i + 3] = [1,...1]
723 //
724 // Expected:
725 // 'vstore2' is removed.
726 // 'vstore3' is not removed.
727 // 'vstore4' is not removed. Such cases are not supported at the moment.
TEST_F(LoadStoreEliminationTest,MergePredecessorVecStores)728 TEST_F(LoadStoreEliminationTest, MergePredecessorVecStores) {
729 HBasicBlock* upper;
730 HBasicBlock* left;
731 HBasicBlock* right;
732 HBasicBlock* down;
733 std::tie(upper, left, right, down) = CreateDiamondShapedCFG();
734
735 // upper: a[i,... i + 3] = [1,...1]
736 HInstruction* vstore1 = AddVecStore(upper, array_, i_);
737 HInstruction* vdata = vstore1->InputAt(2);
738
739 // left: a[i,... i + 3] = [1,...1]
740 HInstruction* vstore2 = AddVecStore(left, array_, i_, vdata);
741
742 // right: a[i+1, ... i + 4] = [1, ... 1]
743 HInstruction* vstore3 = AddVecStore(right, array_, i_add1_, vdata);
744
745 // down: a[i,... i + 3] = [1,...1]
746 HInstruction* vstore4 = AddVecStore(down, array_, i_, vdata);
747
748 PerformLSE();
749
750 ASSERT_TRUE(IsRemoved(vstore2));
751 ASSERT_FALSE(IsRemoved(vstore3));
752 ASSERT_FALSE(IsRemoved(vstore4));
753 }
754
755 // Check that merging works correctly when there are ArraySets in predecessors.
756 //
757 // a[i] = 1
758 // / \
759 // / \
760 // store1: a[i] = 1 store2: a[i+1] = 1
761 // \ /
762 // \ /
763 // store3: a[i] = 1
764 //
765 // Expected:
766 // 'store1' is removed.
767 // 'store2' is not removed.
768 // 'store3' is removed.
TEST_F(LoadStoreEliminationTest,MergePredecessorStores)769 TEST_F(LoadStoreEliminationTest, MergePredecessorStores) {
770 HBasicBlock* upper;
771 HBasicBlock* left;
772 HBasicBlock* right;
773 HBasicBlock* down;
774 std::tie(upper, left, right, down) = CreateDiamondShapedCFG();
775
776 // upper: a[i,... i + 3] = [1,...1]
777 AddArraySet(upper, array_, i_);
778
779 // left: a[i,... i + 3] = [1,...1]
780 HInstruction* store1 = AddArraySet(left, array_, i_);
781
782 // right: a[i+1, ... i + 4] = [1, ... 1]
783 HInstruction* store2 = AddArraySet(right, array_, i_add1_);
784
785 // down: a[i,... i + 3] = [1,...1]
786 HInstruction* store3 = AddArraySet(down, array_, i_);
787
788 PerformLSE();
789
790 ASSERT_TRUE(IsRemoved(store1));
791 ASSERT_FALSE(IsRemoved(store2));
792 ASSERT_TRUE(IsRemoved(store3));
793 }
794
795 // Check that redundant VStore/VLoad are removed from a SIMD loop.
796 //
797 // LOOP BODY
798 // vstore1: a[i,... i + 3] = [1,...1]
799 // vload: x = a[i,... i + 3]
800 // vstore2: b[i,... i + 3] = x
801 // vstore3: a[i,... i + 3] = [1,...1]
802 //
803 // Return 'a' from the method to make it escape.
804 //
805 // Expected:
806 // 'vstore1' is not removed.
807 // 'vload' is removed.
808 // 'vstore2' is removed because 'b' does not escape.
809 // 'vstore3' is removed.
TEST_F(LoadStoreEliminationTest,RedundantVStoreVLoadInLoop)810 TEST_F(LoadStoreEliminationTest, RedundantVStoreVLoadInLoop) {
811 CreateTestControlFlowGraph();
812
813 HInstruction* c0 = graph_->GetIntConstant(0);
814 HInstruction* c128 = graph_->GetIntConstant(128);
815
816 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
817 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
818 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
819
820 ASSERT_TRUE(return_block_->GetLastInstruction()->IsReturnVoid());
821 HInstruction* ret = new (GetAllocator()) HReturn(array_a);
822 return_block_->ReplaceAndRemoveInstructionWith(return_block_->GetLastInstruction(), ret);
823
824 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
825 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
826 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
827
828 // LOOP BODY:
829 // a[i,... i + 3] = [1,...1]
830 // x = a[i,... i + 3]
831 // b[i,... i + 3] = x
832 // a[i,... i + 3] = [1,...1]
833 HInstruction* vstore1 = AddVecStore(loop_, array_a, phi_);
834 HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
835 HInstruction* vstore2 = AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
836 HInstruction* vstore3 = AddVecStore(loop_, array_a, phi_, vstore1->InputAt(2));
837
838 PerformLSE();
839
840 ASSERT_FALSE(IsRemoved(vstore1));
841 ASSERT_TRUE(IsRemoved(vload));
842 ASSERT_TRUE(IsRemoved(vstore2));
843 ASSERT_TRUE(IsRemoved(vstore3));
844 }
845
846 // Loop writes invalidate only possibly aliased heap locations.
TEST_F(LoadStoreEliminationTest,StoreAfterLoopWithSideEffects)847 TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithSideEffects) {
848 CreateTestControlFlowGraph();
849
850 HInstruction* c0 = graph_->GetIntConstant(0);
851 HInstruction* c2 = graph_->GetIntConstant(2);
852 HInstruction* c128 = graph_->GetIntConstant(128);
853
854 // array[0] = 2;
855 // loop:
856 // b[i] = array[i]
857 // array[0] = 2
858 HInstruction* store1 = AddArraySet(entry_block_, array_, c0, c2);
859
860 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
861 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
862 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
863
864 HInstruction* load = AddArrayGet(loop_, array_, phi_);
865 HInstruction* store2 = AddArraySet(loop_, array_b, phi_, load);
866
867 HInstruction* store3 = AddArraySet(return_block_, array_, c0, c2);
868
869 PerformLSE();
870
871 ASSERT_FALSE(IsRemoved(store1));
872 ASSERT_TRUE(IsRemoved(store2));
873 ASSERT_TRUE(IsRemoved(store3));
874 }
875
876 // Loop writes invalidate only possibly aliased heap locations.
TEST_F(LoadStoreEliminationTest,StoreAfterLoopWithSideEffects2)877 TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithSideEffects2) {
878 CreateTestControlFlowGraph();
879
880 // Add another array parameter that may alias with `array_`.
881 // Note: We're not adding it to the suspend check environment.
882 AddParameter(new (GetAllocator()) HParameterValue(
883 graph_->GetDexFile(), dex::TypeIndex(0), 3, DataType::Type::kInt32));
884 HInstruction* array2 = parameters_.back();
885
886 HInstruction* c0 = graph_->GetIntConstant(0);
887 HInstruction* c2 = graph_->GetIntConstant(2);
888
889 // array[0] = 2;
890 // loop:
891 // array2[i] = array[i]
892 // array[0] = 2
893 HInstruction* store1 = AddArraySet(entry_block_, array_, c0, c2);
894
895 HInstruction* load = AddArrayGet(loop_, array_, phi_);
896 HInstruction* store2 = AddArraySet(loop_, array2, phi_, load);
897
898 HInstruction* store3 = AddArraySet(return_block_, array_, c0, c2);
899
900 PerformLSE();
901
902 ASSERT_FALSE(IsRemoved(store1));
903 ASSERT_FALSE(IsRemoved(store2));
904 ASSERT_FALSE(IsRemoved(store3));
905 }
906
907 // As it is not allowed to use defaults for VecLoads, check if there is a new created array
908 // a VecLoad used in a loop and after it is not replaced with a default.
TEST_F(LoadStoreEliminationTest,VLoadDefaultValueInLoopWithoutWriteSideEffects)909 TEST_F(LoadStoreEliminationTest, VLoadDefaultValueInLoopWithoutWriteSideEffects) {
910 CreateTestControlFlowGraph();
911
912 HInstruction* c0 = graph_->GetIntConstant(0);
913 HInstruction* c128 = graph_->GetIntConstant(128);
914
915 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
916 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
917 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
918
919 // LOOP BODY:
920 // v = a[i,... i + 3]
921 // array[0,... 3] = v
922 HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
923 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
924
925 PerformLSE();
926
927 ASSERT_FALSE(IsRemoved(vload));
928 ASSERT_FALSE(IsRemoved(vstore));
929 }
930
931 // As it is not allowed to use defaults for VecLoads, check if there is a new created array
932 // a VecLoad is not replaced with a default.
TEST_F(LoadStoreEliminationTest,VLoadDefaultValue)933 TEST_F(LoadStoreEliminationTest, VLoadDefaultValue) {
934 CreateTestControlFlowGraph();
935
936 HInstruction* c0 = graph_->GetIntConstant(0);
937 HInstruction* c128 = graph_->GetIntConstant(128);
938
939 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
940 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
941 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
942
943 // v = a[0,... 3]
944 // array[0,... 3] = v
945 HInstruction* vload = AddVecLoad(pre_header_, array_a, c0);
946 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
947
948 PerformLSE();
949
950 ASSERT_FALSE(IsRemoved(vload));
951 ASSERT_FALSE(IsRemoved(vstore));
952 }
953
954 // As it is allowed to use defaults for ordinary loads, check if there is a new created array
955 // a load used in a loop and after it is replaced with a default.
TEST_F(LoadStoreEliminationTest,LoadDefaultValueInLoopWithoutWriteSideEffects)956 TEST_F(LoadStoreEliminationTest, LoadDefaultValueInLoopWithoutWriteSideEffects) {
957 CreateTestControlFlowGraph();
958
959 HInstruction* c0 = graph_->GetIntConstant(0);
960 HInstruction* c128 = graph_->GetIntConstant(128);
961
962 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
963 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
964 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
965
966 // LOOP BODY:
967 // v = a[i]
968 // array[0] = v
969 HInstruction* load = AddArrayGet(loop_, array_a, phi_);
970 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
971
972 PerformLSE();
973
974 ASSERT_TRUE(IsRemoved(load));
975 ASSERT_FALSE(IsRemoved(store));
976 }
977
978 // As it is allowed to use defaults for ordinary loads, check if there is a new created array
979 // a load is replaced with a default.
TEST_F(LoadStoreEliminationTest,LoadDefaultValue)980 TEST_F(LoadStoreEliminationTest, LoadDefaultValue) {
981 CreateTestControlFlowGraph();
982
983 HInstruction* c0 = graph_->GetIntConstant(0);
984 HInstruction* c128 = graph_->GetIntConstant(128);
985
986 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
987 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
988 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
989
990 // v = a[0]
991 // array[0] = v
992 HInstruction* load = AddArrayGet(pre_header_, array_a, c0);
993 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
994
995 PerformLSE();
996
997 ASSERT_TRUE(IsRemoved(load));
998 ASSERT_FALSE(IsRemoved(store));
999 }
1000
1001 // As it is not allowed to use defaults for VecLoads but allowed for regular loads,
1002 // check if there is a new created array, a VecLoad and a load used in a loop and after it,
1003 // VecLoad is not replaced with a default but the load is.
TEST_F(LoadStoreEliminationTest,VLoadAndLoadDefaultValueInLoopWithoutWriteSideEffects)1004 TEST_F(LoadStoreEliminationTest, VLoadAndLoadDefaultValueInLoopWithoutWriteSideEffects) {
1005 CreateTestControlFlowGraph();
1006
1007 HInstruction* c0 = graph_->GetIntConstant(0);
1008 HInstruction* c128 = graph_->GetIntConstant(128);
1009
1010 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1011 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1012 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1013
1014 // LOOP BODY:
1015 // v = a[i,... i + 3]
1016 // v1 = a[i]
1017 // array[0,... 3] = v
1018 // array[0] = v1
1019 HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
1020 HInstruction* load = AddArrayGet(loop_, array_a, phi_);
1021 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
1022 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
1023
1024 PerformLSE();
1025
1026 ASSERT_FALSE(IsRemoved(vload));
1027 ASSERT_TRUE(IsRemoved(load));
1028 ASSERT_FALSE(IsRemoved(vstore));
1029 ASSERT_FALSE(IsRemoved(store));
1030 }
1031
1032 // As it is not allowed to use defaults for VecLoads but allowed for regular loads,
1033 // check if there is a new created array, a VecLoad and a load,
1034 // VecLoad is not replaced with a default but the load is.
TEST_F(LoadStoreEliminationTest,VLoadAndLoadDefaultValue)1035 TEST_F(LoadStoreEliminationTest, VLoadAndLoadDefaultValue) {
1036 CreateTestControlFlowGraph();
1037
1038 HInstruction* c0 = graph_->GetIntConstant(0);
1039 HInstruction* c128 = graph_->GetIntConstant(128);
1040
1041 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1042 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1043 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1044
1045 // v = a[0,... 3]
1046 // v1 = a[0]
1047 // array[0,... 3] = v
1048 // array[0] = v1
1049 HInstruction* vload = AddVecLoad(pre_header_, array_a, c0);
1050 HInstruction* load = AddArrayGet(pre_header_, array_a, c0);
1051 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
1052 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
1053
1054 PerformLSE();
1055
1056 ASSERT_FALSE(IsRemoved(vload));
1057 ASSERT_TRUE(IsRemoved(load));
1058 ASSERT_FALSE(IsRemoved(vstore));
1059 ASSERT_FALSE(IsRemoved(store));
1060 }
1061
1062 // It is not allowed to use defaults for VecLoads. However it should not prevent from removing
1063 // loads getting the same value.
1064 // Check a load getting a known value is eliminated (a loop test case).
TEST_F(LoadStoreEliminationTest,VLoadDefaultValueAndVLoadInLoopWithoutWriteSideEffects)1065 TEST_F(LoadStoreEliminationTest, VLoadDefaultValueAndVLoadInLoopWithoutWriteSideEffects) {
1066 CreateTestControlFlowGraph();
1067
1068 HInstruction* c0 = graph_->GetIntConstant(0);
1069 HInstruction* c128 = graph_->GetIntConstant(128);
1070
1071 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1072 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1073 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1074
1075 // LOOP BODY:
1076 // v = a[i,... i + 3]
1077 // v1 = a[i,... i + 3]
1078 // array[0,... 3] = v
1079 // array[128,... 131] = v1
1080 HInstruction* vload1 = AddVecLoad(loop_, array_a, phi_);
1081 HInstruction* vload2 = AddVecLoad(loop_, array_a, phi_);
1082 HInstruction* vstore1 = AddVecStore(return_block_, array_, c0, vload1->AsVecLoad());
1083 HInstruction* vstore2 = AddVecStore(return_block_, array_, c128, vload2->AsVecLoad());
1084
1085 PerformLSE();
1086
1087 ASSERT_FALSE(IsRemoved(vload1));
1088 ASSERT_TRUE(IsRemoved(vload2));
1089 ASSERT_FALSE(IsRemoved(vstore1));
1090 ASSERT_FALSE(IsRemoved(vstore2));
1091 }
1092
1093 // It is not allowed to use defaults for VecLoads. However it should not prevent from removing
1094 // loads getting the same value.
1095 // Check a load getting a known value is eliminated.
TEST_F(LoadStoreEliminationTest,VLoadDefaultValueAndVLoad)1096 TEST_F(LoadStoreEliminationTest, VLoadDefaultValueAndVLoad) {
1097 CreateTestControlFlowGraph();
1098
1099 HInstruction* c0 = graph_->GetIntConstant(0);
1100 HInstruction* c128 = graph_->GetIntConstant(128);
1101
1102 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1103 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1104 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1105
1106 // v = a[0,... 3]
1107 // v1 = a[0,... 3]
1108 // array[0,... 3] = v
1109 // array[128,... 131] = v1
1110 HInstruction* vload1 = AddVecLoad(pre_header_, array_a, c0);
1111 HInstruction* vload2 = AddVecLoad(pre_header_, array_a, c0);
1112 HInstruction* vstore1 = AddVecStore(return_block_, array_, c0, vload1->AsVecLoad());
1113 HInstruction* vstore2 = AddVecStore(return_block_, array_, c128, vload2->AsVecLoad());
1114
1115 PerformLSE();
1116
1117 ASSERT_FALSE(IsRemoved(vload1));
1118 ASSERT_TRUE(IsRemoved(vload2));
1119 ASSERT_FALSE(IsRemoved(vstore1));
1120 ASSERT_FALSE(IsRemoved(vstore2));
1121 }
1122
1123 // Object o = new Obj();
1124 // // Needed because otherwise we short-circuit LSA since GVN would get almost
1125 // // everything other than this. Also since this isn't expected to be a very
1126 // // common pattern it's not worth changing the LSA logic.
1127 // o.foo = 3;
1128 // return o.shadow$_klass_;
TEST_F(LoadStoreEliminationTest,DefaultShadowClass)1129 TEST_F(LoadStoreEliminationTest, DefaultShadowClass) {
1130 CreateGraph();
1131 AdjacencyListGraph blocks(
1132 graph_, GetAllocator(), "entry", "exit", {{"entry", "main"}, {"main", "exit"}});
1133 #define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1134 GET_BLOCK(entry);
1135 GET_BLOCK(main);
1136 GET_BLOCK(exit);
1137 #undef GET_BLOCK
1138
1139 HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
1140 entry->AddInstruction(suspend_check);
1141 entry->AddInstruction(new (GetAllocator()) HGoto());
1142 ManuallyBuildEnvFor(suspend_check, {});
1143
1144 HInstruction* cls = MakeClassLoad();
1145 HInstruction* new_inst = MakeNewInstance(cls);
1146 HInstruction* const_fence = new (GetAllocator()) HConstructorFence(new_inst, 0, GetAllocator());
1147 HInstruction* set_field = MakeIFieldSet(new_inst, graph_->GetIntConstant(33), MemberOffset(32));
1148 HInstruction* get_field =
1149 MakeIFieldGet(new_inst, DataType::Type::kReference, mirror::Object::ClassOffset());
1150 HInstruction* return_val = new (GetAllocator()) HReturn(get_field);
1151 main->AddInstruction(cls);
1152 main->AddInstruction(new_inst);
1153 main->AddInstruction(const_fence);
1154 main->AddInstruction(set_field);
1155 main->AddInstruction(get_field);
1156 main->AddInstruction(return_val);
1157 cls->CopyEnvironmentFrom(suspend_check->GetEnvironment());
1158 new_inst->CopyEnvironmentFrom(suspend_check->GetEnvironment());
1159
1160 SetupExit(exit);
1161
1162 graph_->ClearDominanceInformation();
1163 PerformLSE();
1164
1165 EXPECT_INS_REMOVED(new_inst);
1166 EXPECT_INS_REMOVED(const_fence);
1167 EXPECT_INS_REMOVED(get_field);
1168 EXPECT_INS_REMOVED(set_field);
1169 EXPECT_INS_RETAINED(cls);
1170 EXPECT_INS_EQ(cls, return_val->InputAt(0));
1171 }
1172
1173 // Object o = new Obj();
1174 // // Needed because otherwise we short-circuit LSA since GVN would get almost
1175 // // everything other than this. Also since this isn't expected to be a very
1176 // // common pattern (only a single java function, Object.identityHashCode,
1177 // // ever reads this field) it's not worth changing the LSA logic.
1178 // o.foo = 3;
1179 // return o.shadow$_monitor_;
TEST_F(LoadStoreEliminationTest,DefaultShadowMonitor)1180 TEST_F(LoadStoreEliminationTest, DefaultShadowMonitor) {
1181 CreateGraph();
1182 AdjacencyListGraph blocks(
1183 graph_, GetAllocator(), "entry", "exit", {{"entry", "main"}, {"main", "exit"}});
1184 #define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1185 GET_BLOCK(entry);
1186 GET_BLOCK(main);
1187 GET_BLOCK(exit);
1188 #undef GET_BLOCK
1189
1190 HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
1191 entry->AddInstruction(suspend_check);
1192 entry->AddInstruction(new (GetAllocator()) HGoto());
1193 ManuallyBuildEnvFor(suspend_check, {});
1194
1195 HInstruction* cls = MakeClassLoad();
1196 HInstruction* new_inst = MakeNewInstance(cls);
1197 HInstruction* const_fence = new (GetAllocator()) HConstructorFence(new_inst, 0, GetAllocator());
1198 HInstruction* set_field = MakeIFieldSet(new_inst, graph_->GetIntConstant(33), MemberOffset(32));
1199 HInstruction* get_field =
1200 MakeIFieldGet(new_inst, DataType::Type::kInt32, mirror::Object::MonitorOffset());
1201 HInstruction* return_val = new (GetAllocator()) HReturn(get_field);
1202 main->AddInstruction(cls);
1203 main->AddInstruction(new_inst);
1204 main->AddInstruction(const_fence);
1205 main->AddInstruction(set_field);
1206 main->AddInstruction(get_field);
1207 main->AddInstruction(return_val);
1208 cls->CopyEnvironmentFrom(suspend_check->GetEnvironment());
1209 new_inst->CopyEnvironmentFrom(suspend_check->GetEnvironment());
1210
1211 SetupExit(exit);
1212
1213 graph_->ClearDominanceInformation();
1214 PerformLSE();
1215
1216 EXPECT_INS_REMOVED(new_inst);
1217 EXPECT_INS_REMOVED(const_fence);
1218 EXPECT_INS_REMOVED(get_field);
1219 EXPECT_INS_REMOVED(set_field);
1220 EXPECT_INS_RETAINED(cls);
1221 EXPECT_INS_EQ(graph_->GetIntConstant(0), return_val->InputAt(0));
1222 }
1223
1224 // void DO_CAL() {
1225 // int i = 1;
1226 // int[] w = new int[80];
1227 // int t = 0;
1228 // while (i < 80) {
1229 // w[i] = PLEASE_INTERLEAVE(w[i - 1], 1)
1230 // t = PLEASE_SELECT(w[i], t);
1231 // i++;
1232 // }
1233 // return t;
1234 // }
TEST_F(LoadStoreEliminationTest,ArrayLoopOverlap)1235 TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) {
1236 ScopedObjectAccess soa(Thread::Current());
1237 VariableSizedHandleScope vshs(soa.Self());
1238 CreateGraph(&vshs);
1239 AdjacencyListGraph blocks(graph_,
1240 GetAllocator(),
1241 "entry",
1242 "exit",
1243 { { "entry", "loop_pre_header" },
1244 { "loop_pre_header", "loop_entry" },
1245 { "loop_entry", "loop_body" },
1246 { "loop_entry", "loop_post" },
1247 { "loop_body", "loop_entry" },
1248 { "loop_post", "exit" } });
1249 #define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1250 GET_BLOCK(entry);
1251 GET_BLOCK(loop_pre_header);
1252 GET_BLOCK(loop_entry);
1253 GET_BLOCK(loop_body);
1254 GET_BLOCK(loop_post);
1255 GET_BLOCK(exit);
1256 #undef GET_BLOCK
1257
1258 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1259 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1260 HInstruction* eighty_const = graph_->GetConstant(DataType::Type::kInt32, 80);
1261 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1262 entry->AddInstruction(entry_goto);
1263
1264 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, eighty_const, 0, 0);
1265 HInstruction* pre_header_goto = new (GetAllocator()) HGoto();
1266 loop_pre_header->AddInstruction(alloc_w);
1267 loop_pre_header->AddInstruction(pre_header_goto);
1268 // environment
1269 ManuallyBuildEnvFor(alloc_w, {});
1270
1271 // loop-start
1272 HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
1273 HPhi* t_phi = new (GetAllocator()) HPhi(GetAllocator(), 1, 0, DataType::Type::kInt32);
1274 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
1275 HInstruction* i_cmp_top = new (GetAllocator()) HGreaterThanOrEqual(i_phi, eighty_const);
1276 HInstruction* loop_start_branch = new (GetAllocator()) HIf(i_cmp_top);
1277 loop_entry->AddPhi(i_phi);
1278 loop_entry->AddPhi(t_phi);
1279 loop_entry->AddInstruction(suspend);
1280 loop_entry->AddInstruction(i_cmp_top);
1281 loop_entry->AddInstruction(loop_start_branch);
1282 CHECK_EQ(loop_entry->GetSuccessors().size(), 2u);
1283 if (loop_entry->GetNormalSuccessors()[1] != loop_body) {
1284 loop_entry->SwapSuccessors();
1285 }
1286 CHECK_EQ(loop_entry->GetPredecessors().size(), 2u);
1287 if (loop_entry->GetPredecessors()[0] != loop_pre_header) {
1288 loop_entry->SwapPredecessors();
1289 }
1290 i_phi->AddInput(one_const);
1291 t_phi->AddInput(zero_const);
1292
1293 // environment
1294 ManuallyBuildEnvFor(suspend, { alloc_w, i_phi, t_phi });
1295
1296 // BODY
1297 HInstruction* last_i = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, one_const);
1298 HInstruction* last_get =
1299 new (GetAllocator()) HArrayGet(alloc_w, last_i, DataType::Type::kInt32, 0);
1300 HInvoke* body_value = MakeInvoke(DataType::Type::kInt32, { last_get, one_const });
1301 HInstruction* body_set =
1302 new (GetAllocator()) HArraySet(alloc_w, i_phi, body_value, DataType::Type::kInt32, 0);
1303 HInstruction* body_get =
1304 new (GetAllocator()) HArrayGet(alloc_w, i_phi, DataType::Type::kInt32, 0);
1305 HInvoke* t_next = MakeInvoke(DataType::Type::kInt32, { body_get, t_phi });
1306 HInstruction* i_next = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_phi, one_const);
1307 HInstruction* body_goto = new (GetAllocator()) HGoto();
1308 loop_body->AddInstruction(last_i);
1309 loop_body->AddInstruction(last_get);
1310 loop_body->AddInstruction(body_value);
1311 loop_body->AddInstruction(body_set);
1312 loop_body->AddInstruction(body_get);
1313 loop_body->AddInstruction(t_next);
1314 loop_body->AddInstruction(i_next);
1315 loop_body->AddInstruction(body_goto);
1316 body_value->CopyEnvironmentFrom(suspend->GetEnvironment());
1317
1318 i_phi->AddInput(i_next);
1319 t_phi->AddInput(t_next);
1320 t_next->CopyEnvironmentFrom(suspend->GetEnvironment());
1321
1322 // loop-post
1323 HInstruction* return_inst = new (GetAllocator()) HReturn(t_phi);
1324 loop_post->AddInstruction(return_inst);
1325
1326 // exit
1327 SetupExit(exit);
1328
1329 graph_->ClearDominanceInformation();
1330 graph_->ClearLoopInformation();
1331 PerformLSE();
1332
1333 // TODO Technically this is optimizable. LSE just needs to add phis to keep
1334 // track of the last `N` values set where `N` is how many locations we can go
1335 // back into the array.
1336 if (IsRemoved(last_get)) {
1337 // If we were able to remove the previous read the entire array should be removable.
1338 EXPECT_INS_REMOVED(body_set);
1339 EXPECT_INS_REMOVED(alloc_w);
1340 } else {
1341 // This is the branch we actually take for now. If we rely on being able to
1342 // read the array we'd better remember to write to it as well.
1343 EXPECT_INS_RETAINED(body_set);
1344 }
1345 // The last 'get' should always be removable.
1346 EXPECT_INS_REMOVED(body_get);
1347 }
1348
1349 // void DO_CAL2() {
1350 // int i = 1;
1351 // int[] w = new int[80];
1352 // int t = 0;
1353 // while (i < 80) {
1354 // w[i] = PLEASE_INTERLEAVE(w[i - 1], 1) // <-- removed
1355 // t = PLEASE_SELECT(w[i], t);
1356 // w[i] = PLEASE_INTERLEAVE(w[i - 1], 1) // <-- removed
1357 // t = PLEASE_SELECT(w[i], t);
1358 // w[i] = PLEASE_INTERLEAVE(w[i - 1], 1) // <-- kept
1359 // t = PLEASE_SELECT(w[i], t);
1360 // i++;
1361 // }
1362 // return t;
1363 // }
TEST_F(LoadStoreEliminationTest,ArrayLoopOverlap2)1364 TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) {
1365 ScopedObjectAccess soa(Thread::Current());
1366 VariableSizedHandleScope vshs(soa.Self());
1367 CreateGraph(&vshs);
1368 AdjacencyListGraph blocks(graph_,
1369 GetAllocator(),
1370 "entry",
1371 "exit",
1372 { { "entry", "loop_pre_header" },
1373 { "loop_pre_header", "loop_entry" },
1374 { "loop_entry", "loop_body" },
1375 { "loop_entry", "loop_post" },
1376 { "loop_body", "loop_entry" },
1377 { "loop_post", "exit" } });
1378 #define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1379 GET_BLOCK(entry);
1380 GET_BLOCK(loop_pre_header);
1381 GET_BLOCK(loop_entry);
1382 GET_BLOCK(loop_body);
1383 GET_BLOCK(loop_post);
1384 GET_BLOCK(exit);
1385 #undef GET_BLOCK
1386
1387 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1388 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1389 HInstruction* eighty_const = graph_->GetConstant(DataType::Type::kInt32, 80);
1390 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1391 entry->AddInstruction(entry_goto);
1392
1393 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, eighty_const, 0, 0);
1394 HInstruction* pre_header_goto = new (GetAllocator()) HGoto();
1395 loop_pre_header->AddInstruction(alloc_w);
1396 loop_pre_header->AddInstruction(pre_header_goto);
1397 // environment
1398 ManuallyBuildEnvFor(alloc_w, {});
1399
1400 // loop-start
1401 HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
1402 HPhi* t_phi = new (GetAllocator()) HPhi(GetAllocator(), 1, 0, DataType::Type::kInt32);
1403 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
1404 HInstruction* i_cmp_top = new (GetAllocator()) HGreaterThanOrEqual(i_phi, eighty_const);
1405 HInstruction* loop_start_branch = new (GetAllocator()) HIf(i_cmp_top);
1406 loop_entry->AddPhi(i_phi);
1407 loop_entry->AddPhi(t_phi);
1408 loop_entry->AddInstruction(suspend);
1409 loop_entry->AddInstruction(i_cmp_top);
1410 loop_entry->AddInstruction(loop_start_branch);
1411 CHECK_EQ(loop_entry->GetSuccessors().size(), 2u);
1412 if (loop_entry->GetNormalSuccessors()[1] != loop_body) {
1413 loop_entry->SwapSuccessors();
1414 }
1415 CHECK_EQ(loop_entry->GetPredecessors().size(), 2u);
1416 if (loop_entry->GetPredecessors()[0] != loop_pre_header) {
1417 loop_entry->SwapPredecessors();
1418 }
1419 i_phi->AddInput(one_const);
1420 t_phi->AddInput(zero_const);
1421
1422 // environment
1423 ManuallyBuildEnvFor(suspend, { alloc_w, i_phi, t_phi });
1424
1425 // BODY
1426 HInstruction* last_i = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, one_const);
1427 HInstruction *last_get_1, *last_get_2, *last_get_3;
1428 HInstruction *body_value_1, *body_value_2, *body_value_3;
1429 HInstruction *body_set_1, *body_set_2, *body_set_3;
1430 HInstruction *body_get_1, *body_get_2, *body_get_3;
1431 HInstruction *t_next_1, *t_next_2, *t_next_3;
1432 auto make_instructions = [&](HInstruction* last_t_value) {
1433 HInstruction* last_get =
1434 new (GetAllocator()) HArrayGet(alloc_w, last_i, DataType::Type::kInt32, 0);
1435 HInvoke* body_value = MakeInvoke(DataType::Type::kInt32, { last_get, one_const });
1436 HInstruction* body_set =
1437 new (GetAllocator()) HArraySet(alloc_w, i_phi, body_value, DataType::Type::kInt32, 0);
1438 HInstruction* body_get =
1439 new (GetAllocator()) HArrayGet(alloc_w, i_phi, DataType::Type::kInt32, 0);
1440 HInvoke* t_next = MakeInvoke(DataType::Type::kInt32, { body_get, last_t_value });
1441 loop_body->AddInstruction(last_get);
1442 loop_body->AddInstruction(body_value);
1443 loop_body->AddInstruction(body_set);
1444 loop_body->AddInstruction(body_get);
1445 loop_body->AddInstruction(t_next);
1446 return std::make_tuple(last_get, body_value, body_set, body_get, t_next);
1447 };
1448 std::tie(last_get_1, body_value_1, body_set_1, body_get_1, t_next_1) = make_instructions(t_phi);
1449 std::tie(last_get_2, body_value_2, body_set_2, body_get_2, t_next_2) =
1450 make_instructions(t_next_1);
1451 std::tie(last_get_3, body_value_3, body_set_3, body_get_3, t_next_3) =
1452 make_instructions(t_next_2);
1453 HInstruction* i_next = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_phi, one_const);
1454 HInstruction* body_goto = new (GetAllocator()) HGoto();
1455 loop_body->InsertInstructionBefore(last_i, last_get_1);
1456 loop_body->AddInstruction(i_next);
1457 loop_body->AddInstruction(body_goto);
1458 body_value_1->CopyEnvironmentFrom(suspend->GetEnvironment());
1459 body_value_2->CopyEnvironmentFrom(suspend->GetEnvironment());
1460 body_value_3->CopyEnvironmentFrom(suspend->GetEnvironment());
1461
1462 i_phi->AddInput(i_next);
1463 t_phi->AddInput(t_next_3);
1464 t_next_1->CopyEnvironmentFrom(suspend->GetEnvironment());
1465 t_next_2->CopyEnvironmentFrom(suspend->GetEnvironment());
1466 t_next_3->CopyEnvironmentFrom(suspend->GetEnvironment());
1467
1468 // loop-post
1469 HInstruction* return_inst = new (GetAllocator()) HReturn(t_phi);
1470 loop_post->AddInstruction(return_inst);
1471
1472 // exit
1473 SetupExit(exit);
1474
1475 graph_->ClearDominanceInformation();
1476 graph_->ClearLoopInformation();
1477 PerformLSE();
1478
1479 // TODO Technically this is optimizable. LSE just needs to add phis to keep
1480 // track of the last `N` values set where `N` is how many locations we can go
1481 // back into the array.
1482 if (IsRemoved(last_get_1)) {
1483 // If we were able to remove the previous read the entire array should be removable.
1484 EXPECT_INS_REMOVED(body_set_1);
1485 EXPECT_INS_REMOVED(body_set_2);
1486 EXPECT_INS_REMOVED(body_set_3);
1487 EXPECT_INS_REMOVED(last_get_1);
1488 EXPECT_INS_REMOVED(last_get_2);
1489 EXPECT_INS_REMOVED(alloc_w);
1490 } else {
1491 // This is the branch we actually take for now. If we rely on being able to
1492 // read the array we'd better remember to write to it as well.
1493 EXPECT_INS_RETAINED(body_set_3);
1494 }
1495 // The last 'get' should always be removable.
1496 EXPECT_INS_REMOVED(body_get_1);
1497 EXPECT_INS_REMOVED(body_get_2);
1498 EXPECT_INS_REMOVED(body_get_3);
1499 // shadowed writes should always be removed
1500 EXPECT_INS_REMOVED(body_set_1);
1501 EXPECT_INS_REMOVED(body_set_2);
1502 }
1503
TEST_F(LoadStoreEliminationTest,ArrayNonLoopPhi)1504 TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) {
1505 ScopedObjectAccess soa(Thread::Current());
1506 VariableSizedHandleScope vshs(soa.Self());
1507 CreateGraph(&vshs);
1508 AdjacencyListGraph blocks(graph_,
1509 GetAllocator(),
1510 "entry",
1511 "exit",
1512 { { "entry", "start" },
1513 { "start", "left" },
1514 { "start", "right" },
1515 { "left", "ret" },
1516 { "right", "ret" },
1517 { "ret", "exit" } });
1518 #define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1519 GET_BLOCK(entry);
1520 GET_BLOCK(start);
1521 GET_BLOCK(left);
1522 GET_BLOCK(right);
1523 GET_BLOCK(ret);
1524 GET_BLOCK(exit);
1525 #undef GET_BLOCK
1526
1527 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1528 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1529 HInstruction* two_const = graph_->GetConstant(DataType::Type::kInt32, 2);
1530 HInstruction* param = MakeParam(DataType::Type::kBool);
1531
1532 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1533 entry->AddInstruction(entry_goto);
1534
1535 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, two_const, 0, 0);
1536 HInstruction* branch = new (GetAllocator()) HIf(param);
1537 start->AddInstruction(alloc_w);
1538 start->AddInstruction(branch);
1539 // environment
1540 ManuallyBuildEnvFor(alloc_w, {});
1541
1542 // left
1543 HInvoke* left_value = MakeInvoke(DataType::Type::kInt32, { zero_const });
1544 HInstruction* left_set_1 =
1545 new (GetAllocator()) HArraySet(alloc_w, zero_const, left_value, DataType::Type::kInt32, 0);
1546 HInstruction* left_set_2 =
1547 new (GetAllocator()) HArraySet(alloc_w, one_const, zero_const, DataType::Type::kInt32, 0);
1548 HInstruction* left_goto = new (GetAllocator()) HGoto();
1549 left->AddInstruction(left_value);
1550 left->AddInstruction(left_set_1);
1551 left->AddInstruction(left_set_2);
1552 left->AddInstruction(left_goto);
1553 ManuallyBuildEnvFor(left_value, { alloc_w });
1554
1555 // right
1556 HInvoke* right_value = MakeInvoke(DataType::Type::kInt32, { one_const });
1557 HInstruction* right_set_1 =
1558 new (GetAllocator()) HArraySet(alloc_w, zero_const, right_value, DataType::Type::kInt32, 0);
1559 HInstruction* right_set_2 =
1560 new (GetAllocator()) HArraySet(alloc_w, one_const, zero_const, DataType::Type::kInt32, 0);
1561 HInstruction* right_goto = new (GetAllocator()) HGoto();
1562 right->AddInstruction(right_value);
1563 right->AddInstruction(right_set_1);
1564 right->AddInstruction(right_set_2);
1565 right->AddInstruction(right_goto);
1566 ManuallyBuildEnvFor(right_value, { alloc_w });
1567
1568 // ret
1569 HInstruction* read_1 =
1570 new (GetAllocator()) HArrayGet(alloc_w, zero_const, DataType::Type::kInt32, 0);
1571 HInstruction* read_2 =
1572 new (GetAllocator()) HArrayGet(alloc_w, one_const, DataType::Type::kInt32, 0);
1573 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, read_1, read_2);
1574 HInstruction* return_inst = new (GetAllocator()) HReturn(add);
1575 ret->AddInstruction(read_1);
1576 ret->AddInstruction(read_2);
1577 ret->AddInstruction(add);
1578 ret->AddInstruction(return_inst);
1579
1580 // exit
1581 SetupExit(exit);
1582
1583 graph_->ClearDominanceInformation();
1584 graph_->ClearLoopInformation();
1585 PerformLSE();
1586
1587 EXPECT_INS_REMOVED(read_1);
1588 EXPECT_INS_REMOVED(read_2);
1589 EXPECT_INS_REMOVED(left_set_1);
1590 EXPECT_INS_REMOVED(left_set_2);
1591 EXPECT_INS_REMOVED(right_set_1);
1592 EXPECT_INS_REMOVED(right_set_2);
1593 EXPECT_INS_REMOVED(alloc_w);
1594
1595 EXPECT_INS_RETAINED(left_value);
1596 EXPECT_INS_RETAINED(right_value);
1597 }
1598
TEST_F(LoadStoreEliminationTest,ArrayMergeDefault)1599 TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) {
1600 ScopedObjectAccess soa(Thread::Current());
1601 VariableSizedHandleScope vshs(soa.Self());
1602 CreateGraph(&vshs);
1603 AdjacencyListGraph blocks(graph_,
1604 GetAllocator(),
1605 "entry",
1606 "exit",
1607 { { "entry", "start" },
1608 { "start", "left" },
1609 { "start", "right" },
1610 { "left", "ret" },
1611 { "right", "ret" },
1612 { "ret", "exit" } });
1613 #define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1614 GET_BLOCK(entry);
1615 GET_BLOCK(start);
1616 GET_BLOCK(left);
1617 GET_BLOCK(right);
1618 GET_BLOCK(ret);
1619 GET_BLOCK(exit);
1620 #undef GET_BLOCK
1621
1622 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1623 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1624 HInstruction* two_const = graph_->GetConstant(DataType::Type::kInt32, 2);
1625 HInstruction* param = MakeParam(DataType::Type::kBool);
1626 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1627
1628 entry->AddInstruction(entry_goto);
1629
1630 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, two_const, 0, 0);
1631 HInstruction* branch = new (GetAllocator()) HIf(param);
1632 start->AddInstruction(alloc_w);
1633 start->AddInstruction(branch);
1634 // environment
1635 ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction));
1636 ManuallyBuildEnvFor(alloc_w, {});
1637
1638 // left
1639 HInstruction* left_set_1 =
1640 new (GetAllocator()) HArraySet(alloc_w, zero_const, one_const, DataType::Type::kInt32, 0);
1641 HInstruction* left_set_2 =
1642 new (GetAllocator()) HArraySet(alloc_w, zero_const, zero_const, DataType::Type::kInt32, 0);
1643 HInstruction* left_goto = new (GetAllocator()) HGoto();
1644 left->AddInstruction(left_set_1);
1645 left->AddInstruction(left_set_2);
1646 left->AddInstruction(left_goto);
1647
1648 // right
1649 HInstruction* right_set_1 =
1650 new (GetAllocator()) HArraySet(alloc_w, one_const, one_const, DataType::Type::kInt32, 0);
1651 HInstruction* right_set_2 =
1652 new (GetAllocator()) HArraySet(alloc_w, one_const, zero_const, DataType::Type::kInt32, 0);
1653 HInstruction* right_goto = new (GetAllocator()) HGoto();
1654 right->AddInstruction(right_set_1);
1655 right->AddInstruction(right_set_2);
1656 right->AddInstruction(right_goto);
1657
1658 // ret
1659 HInstruction* read_1 =
1660 new (GetAllocator()) HArrayGet(alloc_w, zero_const, DataType::Type::kInt32, 0);
1661 HInstruction* read_2 =
1662 new (GetAllocator()) HArrayGet(alloc_w, one_const, DataType::Type::kInt32, 0);
1663 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, read_1, read_2);
1664 HInstruction* return_inst = new (GetAllocator()) HReturn(add);
1665 ret->AddInstruction(read_1);
1666 ret->AddInstruction(read_2);
1667 ret->AddInstruction(add);
1668 ret->AddInstruction(return_inst);
1669
1670 // exit
1671 SetupExit(exit);
1672
1673 graph_->ClearDominanceInformation();
1674 graph_->ClearLoopInformation();
1675 PerformLSE();
1676
1677 EXPECT_INS_REMOVED(read_1);
1678 EXPECT_INS_REMOVED(read_2);
1679 EXPECT_INS_REMOVED(left_set_1);
1680 EXPECT_INS_REMOVED(left_set_2);
1681 EXPECT_INS_REMOVED(right_set_1);
1682 EXPECT_INS_REMOVED(right_set_2);
1683 EXPECT_INS_REMOVED(alloc_w);
1684 }
1685
1686 // Regression test for b/187487955.
1687 // We previusly failed to consider aliasing between an array location
1688 // with index `idx` defined in the loop (such as a loop Phi) and another
1689 // array location with index `idx + constant`. This could have led to
1690 // replacing the load with, for example, the default value 0.
TEST_F(LoadStoreEliminationTest,ArrayLoopAliasing1)1691 TEST_F(LoadStoreEliminationTest, ArrayLoopAliasing1) {
1692 ScopedObjectAccess soa(Thread::Current());
1693 VariableSizedHandleScope vshs(soa.Self());
1694 CreateGraph(&vshs);
1695 AdjacencyListGraph blocks(graph_,
1696 GetAllocator(),
1697 "entry",
1698 "exit",
1699 { { "entry", "preheader" },
1700 { "preheader", "loop" },
1701 { "loop", "body" },
1702 { "body", "loop" },
1703 { "loop", "ret" },
1704 { "ret", "exit" } });
1705 #define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1706 GET_BLOCK(entry);
1707 GET_BLOCK(preheader);
1708 GET_BLOCK(loop);
1709 GET_BLOCK(body);
1710 GET_BLOCK(ret);
1711 GET_BLOCK(exit);
1712 #undef GET_BLOCK
1713 HInstruction* n = MakeParam(DataType::Type::kInt32);
1714 HInstruction* c0 = graph_->GetIntConstant(0);
1715 HInstruction* c1 = graph_->GetIntConstant(1);
1716
1717 // entry
1718 HInstruction* cls = MakeClassLoad();
1719 HInstruction* array = new (GetAllocator()) HNewArray(
1720 cls, n, /*dex_pc=*/ 0u, DataType::SizeShift(DataType::Type::kInt32));
1721 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1722 entry->AddInstruction(cls);
1723 entry->AddInstruction(array);
1724 entry->AddInstruction(entry_goto);
1725 ManuallyBuildEnvFor(cls, {});
1726 ManuallyBuildEnvFor(array, {});
1727
1728 HInstruction* preheader_goto = new (GetAllocator()) HGoto();
1729 preheader->AddInstruction(preheader_goto);
1730
1731 // loop
1732 HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
1733 HInstruction* loop_suspend_check = new (GetAllocator()) HSuspendCheck();
1734 HInstruction* loop_cond = new (GetAllocator()) HLessThan(i_phi, n);
1735 HIf* loop_if = new (GetAllocator()) HIf(loop_cond);
1736 loop->AddPhi(i_phi);
1737 loop->AddInstruction(loop_suspend_check);
1738 loop->AddInstruction(loop_cond);
1739 loop->AddInstruction(loop_if);
1740 CHECK(loop_if->IfTrueSuccessor() == body);
1741 ManuallyBuildEnvFor(loop_suspend_check, {});
1742
1743 // body
1744 HInstruction* body_set =
1745 new (GetAllocator()) HArraySet(array, i_phi, i_phi, DataType::Type::kInt32, /*dex_pc=*/ 0u);
1746 HInstruction* body_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_phi, c1);
1747 HInstruction* body_goto = new (GetAllocator()) HGoto();
1748 body->AddInstruction(body_set);
1749 body->AddInstruction(body_add);
1750 body->AddInstruction(body_goto);
1751
1752 // i_phi inputs
1753 i_phi->AddInput(c0);
1754 i_phi->AddInput(body_add);
1755
1756 // ret
1757 HInstruction* ret_sub = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, c1);
1758 HInstruction* ret_get =
1759 new (GetAllocator()) HArrayGet(array, ret_sub, DataType::Type::kInt32, /*dex_pc=*/ 0);
1760 HInstruction* ret_return = new (GetAllocator()) HReturn(ret_get);
1761 ret->AddInstruction(ret_sub);
1762 ret->AddInstruction(ret_get);
1763 ret->AddInstruction(ret_return);
1764
1765 // exit
1766 SetupExit(exit);
1767
1768 graph_->ClearDominanceInformation();
1769 graph_->ClearLoopInformation();
1770 PerformLSE();
1771
1772 EXPECT_INS_RETAINED(cls);
1773 EXPECT_INS_RETAINED(array);
1774 EXPECT_INS_RETAINED(body_set);
1775 EXPECT_INS_RETAINED(ret_get);
1776 }
1777
1778 // Regression test for b/187487955.
1779 // Similar to the `ArrayLoopAliasing1` test above but with additional load
1780 // that marks a loop Phi placeholder as kept which used to trigger a DCHECK().
1781 // There is also an LSE run-test for this but it relies on BCE eliminating
1782 // BoundsCheck instructions and adds extra code in loop body to avoid
1783 // loop unrolling. This gtest does not need to jump through those hoops
1784 // as we do not unnecessarily run those optimization passes.
TEST_F(LoadStoreEliminationTest,ArrayLoopAliasing2)1785 TEST_F(LoadStoreEliminationTest, ArrayLoopAliasing2) {
1786 ScopedObjectAccess soa(Thread::Current());
1787 VariableSizedHandleScope vshs(soa.Self());
1788 CreateGraph(&vshs);
1789 AdjacencyListGraph blocks(graph_,
1790 GetAllocator(),
1791 "entry",
1792 "exit",
1793 { { "entry", "preheader" },
1794 { "preheader", "loop" },
1795 { "loop", "body" },
1796 { "body", "loop" },
1797 { "loop", "ret" },
1798 { "ret", "exit" } });
1799 #define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1800 GET_BLOCK(entry);
1801 GET_BLOCK(preheader);
1802 GET_BLOCK(loop);
1803 GET_BLOCK(body);
1804 GET_BLOCK(ret);
1805 GET_BLOCK(exit);
1806 #undef GET_BLOCK
1807 HInstruction* n = MakeParam(DataType::Type::kInt32);
1808 HInstruction* c0 = graph_->GetIntConstant(0);
1809 HInstruction* c1 = graph_->GetIntConstant(1);
1810
1811 // entry
1812 HInstruction* cls = MakeClassLoad();
1813 HInstruction* array = new (GetAllocator()) HNewArray(
1814 cls, n, /*dex_pc=*/ 0u, DataType::SizeShift(DataType::Type::kInt32));
1815 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1816 entry->AddInstruction(cls);
1817 entry->AddInstruction(array);
1818 entry->AddInstruction(entry_goto);
1819 ManuallyBuildEnvFor(cls, {});
1820 ManuallyBuildEnvFor(array, {});
1821
1822 HInstruction* preheader_goto = new (GetAllocator()) HGoto();
1823 preheader->AddInstruction(preheader_goto);
1824
1825 // loop
1826 HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
1827 HInstruction* loop_suspend_check = new (GetAllocator()) HSuspendCheck();
1828 HInstruction* loop_cond = new (GetAllocator()) HLessThan(i_phi, n);
1829 HIf* loop_if = new (GetAllocator()) HIf(loop_cond);
1830 loop->AddPhi(i_phi);
1831 loop->AddInstruction(loop_suspend_check);
1832 loop->AddInstruction(loop_cond);
1833 loop->AddInstruction(loop_if);
1834 CHECK(loop_if->IfTrueSuccessor() == body);
1835 ManuallyBuildEnvFor(loop_suspend_check, {});
1836
1837 // body
1838 HInstruction* body_set =
1839 new (GetAllocator()) HArraySet(array, i_phi, i_phi, DataType::Type::kInt32, /*dex_pc=*/ 0u);
1840 HInstruction* body_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_phi, c1);
1841 HInstruction* body_goto = new (GetAllocator()) HGoto();
1842 body->AddInstruction(body_set);
1843 body->AddInstruction(body_add);
1844 body->AddInstruction(body_goto);
1845
1846 // i_phi inputs
1847 i_phi->AddInput(c0);
1848 i_phi->AddInput(body_add);
1849
1850 // ret
1851 HInstruction* ret_sub = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, c1);
1852 HInstruction* ret_get1 =
1853 new (GetAllocator()) HArrayGet(array, ret_sub, DataType::Type::kInt32, /*dex_pc=*/ 0);
1854 HInstruction* ret_get2 =
1855 new (GetAllocator()) HArrayGet(array, i_phi, DataType::Type::kInt32, /*dex_pc=*/ 0);
1856 HInstruction* ret_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, ret_get1, ret_get2);
1857 HInstruction* ret_return = new (GetAllocator()) HReturn(ret_add);
1858 ret->AddInstruction(ret_sub);
1859 ret->AddInstruction(ret_get1);
1860 ret->AddInstruction(ret_get2);
1861 ret->AddInstruction(ret_add);
1862 ret->AddInstruction(ret_return);
1863
1864 // exit
1865 SetupExit(exit);
1866
1867 graph_->ClearDominanceInformation();
1868 graph_->ClearLoopInformation();
1869 PerformLSE();
1870
1871 EXPECT_INS_RETAINED(cls);
1872 EXPECT_INS_RETAINED(array);
1873 EXPECT_INS_RETAINED(body_set);
1874 EXPECT_INS_RETAINED(ret_get1);
1875 EXPECT_INS_RETAINED(ret_get2);
1876 }
1877
1878 // // ENTRY
1879 // obj = new Obj();
1880 // // ALL should be kept
1881 // switch (parameter_value) {
1882 // case 1:
1883 // // Case1
1884 // obj.field = 1;
1885 // call_func(obj);
1886 // break;
1887 // case 2:
1888 // // Case2
1889 // obj.field = 2;
1890 // call_func(obj);
1891 // // We don't know what obj.field is now we aren't able to eliminate the read below!
1892 // break;
1893 // default:
1894 // // Case3
1895 // // TODO This only happens because of limitations on our LSE which is unable
1896 // // to materialize co-dependent loop and non-loop phis.
1897 // // Ideally we'd want to generate
1898 // // P1 = PHI[3, loop_val]
1899 // // while (test()) {
1900 // // if (test2()) { goto; } else { goto; }
1901 // // loop_val = [P1, 5]
1902 // // }
1903 // // Currently we aren't able to unfortunately.
1904 // obj.field = 3;
1905 // while (test()) {
1906 // if (test2()) { } else { obj.field = 5; }
1907 // }
1908 // break;
1909 // }
1910 // EXIT
1911 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialUnknownMerge)1912 TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) {
1913 CreateGraph();
1914 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
1915 "exit",
1916 {{"entry", "bswitch"},
1917 {"bswitch", "case1"},
1918 {"bswitch", "case2"},
1919 {"bswitch", "case3"},
1920 {"case1", "breturn"},
1921 {"case2", "breturn"},
1922 {"case3", "loop_pre_header"},
1923 {"loop_pre_header", "loop_header"},
1924 {"loop_header", "loop_body"},
1925 {"loop_body", "loop_if_left"},
1926 {"loop_body", "loop_if_right"},
1927 {"loop_if_left", "loop_end"},
1928 {"loop_if_right", "loop_end"},
1929 {"loop_end", "loop_header"},
1930 {"loop_header", "breturn"},
1931 {"breturn", "exit"}}));
1932 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
1933 GET_BLOCK(entry);
1934 GET_BLOCK(bswitch);
1935 GET_BLOCK(exit);
1936 GET_BLOCK(breturn);
1937 GET_BLOCK(case1);
1938 GET_BLOCK(case2);
1939 GET_BLOCK(case3);
1940
1941 GET_BLOCK(loop_pre_header);
1942 GET_BLOCK(loop_header);
1943 GET_BLOCK(loop_body);
1944 GET_BLOCK(loop_if_left);
1945 GET_BLOCK(loop_if_right);
1946 GET_BLOCK(loop_end);
1947 #undef GET_BLOCK
1948 HInstruction* switch_val = MakeParam(DataType::Type::kInt32);
1949 HInstruction* c1 = graph_->GetIntConstant(1);
1950 HInstruction* c2 = graph_->GetIntConstant(2);
1951 HInstruction* c3 = graph_->GetIntConstant(3);
1952 HInstruction* c5 = graph_->GetIntConstant(5);
1953
1954 HInstruction* cls = MakeClassLoad();
1955 HInstruction* new_inst = MakeNewInstance(cls);
1956 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1957 entry->AddInstruction(cls);
1958 entry->AddInstruction(new_inst);
1959 entry->AddInstruction(entry_goto);
1960 ManuallyBuildEnvFor(cls, {});
1961 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
1962
1963 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val);
1964 bswitch->AddInstruction(switch_inst);
1965
1966 HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32));
1967 HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst });
1968 HInstruction* goto_c1 = new (GetAllocator()) HGoto();
1969 case1->AddInstruction(write_c1);
1970 case1->AddInstruction(call_c1);
1971 case1->AddInstruction(goto_c1);
1972 call_c1->CopyEnvironmentFrom(cls->GetEnvironment());
1973
1974 HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32));
1975 HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst });
1976 HInstruction* goto_c2 = new (GetAllocator()) HGoto();
1977 case2->AddInstruction(write_c2);
1978 case2->AddInstruction(call_c2);
1979 case2->AddInstruction(goto_c2);
1980 call_c2->CopyEnvironmentFrom(cls->GetEnvironment());
1981
1982 HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32));
1983 HInstruction* goto_c3 = new (GetAllocator()) HGoto();
1984 case3->AddInstruction(write_c3);
1985 case3->AddInstruction(goto_c3);
1986
1987 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
1988 loop_pre_header->AddInstruction(goto_preheader);
1989
1990 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
1991 HInstruction* call_loop_header = MakeInvoke(DataType::Type::kBool, {});
1992 HInstruction* if_loop_header = new (GetAllocator()) HIf(call_loop_header);
1993 loop_header->AddInstruction(suspend_check_header);
1994 loop_header->AddInstruction(call_loop_header);
1995 loop_header->AddInstruction(if_loop_header);
1996 call_loop_header->CopyEnvironmentFrom(cls->GetEnvironment());
1997 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
1998
1999 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
2000 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
2001 loop_body->AddInstruction(call_loop_body);
2002 loop_body->AddInstruction(if_loop_body);
2003 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
2004
2005 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
2006 loop_if_left->AddInstruction(goto_loop_left);
2007
2008 HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32));
2009 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
2010 loop_if_right->AddInstruction(write_loop_right);
2011 loop_if_right->AddInstruction(goto_loop_right);
2012
2013 HInstruction* goto_loop_end = new (GetAllocator()) HGoto();
2014 loop_end->AddInstruction(goto_loop_end);
2015
2016 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
2017 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
2018 breturn->AddInstruction(read_bottom);
2019 breturn->AddInstruction(return_exit);
2020
2021 SetupExit(exit);
2022
2023 // PerformLSE expects this to be empty.
2024 graph_->ClearDominanceInformation();
2025 LOG(INFO) << "Pre LSE " << blks;
2026 PerformLSENoPartial();
2027
2028 EXPECT_INS_RETAINED(read_bottom);
2029 EXPECT_INS_RETAINED(write_c1);
2030 EXPECT_INS_RETAINED(write_c2);
2031 EXPECT_INS_RETAINED(write_c3);
2032 EXPECT_INS_RETAINED(write_loop_right);
2033 }
2034
2035 // // ENTRY
2036 // obj = new Obj();
2037 // if (parameter_value) {
2038 // // LEFT
2039 // obj.field = 1;
2040 // call_func(obj);
2041 // foo_r = obj.field
2042 // } else {
2043 // // TO BE ELIMINATED
2044 // obj.field = 2;
2045 // // RIGHT
2046 // // TO BE ELIMINATED
2047 // foo_l = obj.field;
2048 // }
2049 // EXIT
2050 // return PHI(foo_l, foo_r)
TEST_F(LoadStoreEliminationTest,PartialLoadElimination)2051 TEST_F(LoadStoreEliminationTest, PartialLoadElimination) {
2052 ScopedObjectAccess soa(Thread::Current());
2053 VariableSizedHandleScope vshs(soa.Self());
2054 CreateGraph(&vshs);
2055 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2056 "exit_REAL",
2057 { { "entry", "left" },
2058 { "entry", "right" },
2059 { "left", "exit" },
2060 { "right", "exit" },
2061 { "exit", "exit_REAL" } }));
2062 HBasicBlock* entry = blks.Get("entry");
2063 HBasicBlock* left = blks.Get("left");
2064 HBasicBlock* right = blks.Get("right");
2065 HBasicBlock* exit = blks.Get("exit");
2066 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
2067 HInstruction* c1 = graph_->GetIntConstant(1);
2068 HInstruction* c2 = graph_->GetIntConstant(2);
2069
2070 HInstruction* cls = MakeClassLoad();
2071 HInstruction* new_inst = MakeNewInstance(cls);
2072 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
2073 entry->AddInstruction(cls);
2074 entry->AddInstruction(new_inst);
2075 entry->AddInstruction(if_inst);
2076 ManuallyBuildEnvFor(cls, {});
2077 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
2078
2079 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
2080 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
2081 HInstruction* read_left = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(16));
2082 HInstruction* goto_left = new (GetAllocator()) HGoto();
2083 left->AddInstruction(write_left);
2084 left->AddInstruction(call_left);
2085 left->AddInstruction(read_left);
2086 left->AddInstruction(goto_left);
2087 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
2088
2089 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(16));
2090 HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(16));
2091 HInstruction* goto_right = new (GetAllocator()) HGoto();
2092 right->AddInstruction(write_right);
2093 right->AddInstruction(read_right);
2094 right->AddInstruction(goto_right);
2095
2096 HInstruction* phi_final = MakePhi({read_left, read_right});
2097 HInstruction* return_exit = new (GetAllocator()) HReturn(phi_final);
2098 exit->AddPhi(phi_final->AsPhi());
2099 exit->AddInstruction(return_exit);
2100
2101 // PerformLSE expects this to be empty.
2102 graph_->ClearDominanceInformation();
2103 PerformLSE();
2104
2105 ASSERT_TRUE(IsRemoved(read_right));
2106 ASSERT_FALSE(IsRemoved(read_left));
2107 ASSERT_FALSE(IsRemoved(phi_final));
2108 ASSERT_TRUE(phi_final->GetInputs()[1] == c2);
2109 ASSERT_TRUE(phi_final->GetInputs()[0] == read_left);
2110 ASSERT_TRUE(IsRemoved(write_right));
2111 }
2112
2113 // // ENTRY
2114 // obj = new Obj();
2115 // if (parameter_value) {
2116 // // LEFT
2117 // obj.field = 1;
2118 // call_func(obj);
2119 // // We don't know what obj.field is now we aren't able to eliminate the read below!
2120 // } else {
2121 // // DO NOT ELIMINATE
2122 // obj.field = 2;
2123 // // RIGHT
2124 // }
2125 // EXIT
2126 // return obj.field
2127 // This test runs with partial LSE disabled.
TEST_F(LoadStoreEliminationTest,PartialLoadPreserved)2128 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved) {
2129 ScopedObjectAccess soa(Thread::Current());
2130 VariableSizedHandleScope vshs(soa.Self());
2131 CreateGraph(&vshs);
2132 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2133 "exit_REAL",
2134 { { "entry", "left" },
2135 { "entry", "right" },
2136 { "left", "exit" },
2137 { "right", "exit" },
2138 { "exit", "exit_REAL" } }));
2139 HBasicBlock* entry = blks.Get("entry");
2140 HBasicBlock* left = blks.Get("left");
2141 HBasicBlock* right = blks.Get("right");
2142 HBasicBlock* exit = blks.Get("exit");
2143 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
2144 HInstruction* c1 = graph_->GetIntConstant(1);
2145 HInstruction* c2 = graph_->GetIntConstant(2);
2146
2147 HInstruction* cls = MakeClassLoad();
2148 HInstruction* new_inst = MakeNewInstance(cls);
2149 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
2150 entry->AddInstruction(cls);
2151 entry->AddInstruction(new_inst);
2152 entry->AddInstruction(if_inst);
2153 ManuallyBuildEnvFor(cls, {});
2154 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
2155
2156 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
2157 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
2158 HInstruction* goto_left = new (GetAllocator()) HGoto();
2159 left->AddInstruction(write_left);
2160 left->AddInstruction(call_left);
2161 left->AddInstruction(goto_left);
2162 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
2163
2164 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
2165 HInstruction* goto_right = new (GetAllocator()) HGoto();
2166 right->AddInstruction(write_right);
2167 right->AddInstruction(goto_right);
2168
2169 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
2170 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
2171 exit->AddInstruction(read_bottom);
2172 exit->AddInstruction(return_exit);
2173 // PerformLSE expects this to be empty.
2174 graph_->ClearDominanceInformation();
2175 PerformLSENoPartial();
2176
2177 EXPECT_INS_RETAINED(read_bottom) << *read_bottom;
2178 EXPECT_INS_RETAINED(write_right) << *write_right;
2179 }
2180
2181 // // ENTRY
2182 // obj = new Obj();
2183 // if (parameter_value) {
2184 // // LEFT
2185 // obj.field = 1;
2186 // call_func(obj);
2187 // // We don't know what obj.field is now we aren't able to eliminate the read below!
2188 // } else {
2189 // // DO NOT ELIMINATE
2190 // if (param2) {
2191 // obj.field = 2;
2192 // } else {
2193 // obj.field = 3;
2194 // }
2195 // // RIGHT
2196 // }
2197 // EXIT
2198 // return obj.field
2199 // NB This test is for non-partial LSE flow. Normally the obj.field writes will be removed
TEST_F(LoadStoreEliminationTest,PartialLoadPreserved2)2200 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) {
2201 ScopedObjectAccess soa(Thread::Current());
2202 VariableSizedHandleScope vshs(soa.Self());
2203 CreateGraph(&vshs);
2204 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2205 "exit_REAL",
2206 { { "entry", "left" },
2207 { "entry", "right_start" },
2208 { "left", "exit" },
2209 { "right_start", "right_first" },
2210 { "right_start", "right_second" },
2211 { "right_first", "right_end" },
2212 { "right_second", "right_end" },
2213 { "right_end", "exit" },
2214 { "exit", "exit_REAL" } }));
2215 HBasicBlock* entry = blks.Get("entry");
2216 HBasicBlock* left = blks.Get("left");
2217 HBasicBlock* right_start = blks.Get("right_start");
2218 HBasicBlock* right_first = blks.Get("right_first");
2219 HBasicBlock* right_second = blks.Get("right_second");
2220 HBasicBlock* right_end = blks.Get("right_end");
2221 HBasicBlock* exit = blks.Get("exit");
2222 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
2223 HInstruction* bool_value_2 = MakeParam(DataType::Type::kBool);
2224 HInstruction* c1 = graph_->GetIntConstant(1);
2225 HInstruction* c2 = graph_->GetIntConstant(2);
2226 HInstruction* c3 = graph_->GetIntConstant(3);
2227
2228 HInstruction* cls = MakeClassLoad();
2229 HInstruction* new_inst = MakeNewInstance(cls);
2230 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
2231 entry->AddInstruction(cls);
2232 entry->AddInstruction(new_inst);
2233 entry->AddInstruction(if_inst);
2234 ManuallyBuildEnvFor(cls, {});
2235 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
2236
2237 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
2238 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
2239 HInstruction* goto_left = new (GetAllocator()) HGoto();
2240 left->AddInstruction(write_left);
2241 left->AddInstruction(call_left);
2242 left->AddInstruction(goto_left);
2243 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
2244
2245 HInstruction* right_if = new (GetAllocator()) HIf(bool_value_2);
2246 right_start->AddInstruction(right_if);
2247
2248 HInstruction* write_right_first = MakeIFieldSet(new_inst, c2, MemberOffset(32));
2249 HInstruction* goto_right_first = new (GetAllocator()) HGoto();
2250 right_first->AddInstruction(write_right_first);
2251 right_first->AddInstruction(goto_right_first);
2252
2253 HInstruction* write_right_second = MakeIFieldSet(new_inst, c3, MemberOffset(32));
2254 HInstruction* goto_right_second = new (GetAllocator()) HGoto();
2255 right_second->AddInstruction(write_right_second);
2256 right_second->AddInstruction(goto_right_second);
2257
2258 HInstruction* goto_right_end = new (GetAllocator()) HGoto();
2259 right_end->AddInstruction(goto_right_end);
2260
2261 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
2262 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
2263 exit->AddInstruction(read_bottom);
2264 exit->AddInstruction(return_exit);
2265 // PerformLSE expects this to be empty.
2266 graph_->ClearDominanceInformation();
2267 PerformLSENoPartial();
2268
2269 EXPECT_INS_RETAINED(read_bottom);
2270 EXPECT_INS_RETAINED(write_right_first);
2271 EXPECT_INS_RETAINED(write_right_second);
2272 }
2273
2274 // // ENTRY
2275 // obj = new Obj();
2276 // if (parameter_value) {
2277 // // LEFT
2278 // // DO NOT ELIMINATE
2279 // escape(obj);
2280 // obj.field = 1;
2281 // } else {
2282 // // RIGHT
2283 // // ELIMINATE
2284 // obj.field = 2;
2285 // }
2286 // EXIT
2287 // ELIMINATE
2288 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialLoadElimination2)2289 TEST_F(LoadStoreEliminationTest, PartialLoadElimination2) {
2290 ScopedObjectAccess soa(Thread::Current());
2291 VariableSizedHandleScope vshs(soa.Self());
2292 CreateGraph(&vshs);
2293 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2294 "exit",
2295 {{"entry", "left"},
2296 {"entry", "right"},
2297 {"left", "breturn"},
2298 {"right", "breturn"},
2299 {"breturn", "exit"}}));
2300 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
2301 GET_BLOCK(entry);
2302 GET_BLOCK(exit);
2303 GET_BLOCK(breturn);
2304 GET_BLOCK(left);
2305 GET_BLOCK(right);
2306 #undef GET_BLOCK
2307 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
2308 HInstruction* c1 = graph_->GetIntConstant(1);
2309 HInstruction* c2 = graph_->GetIntConstant(2);
2310
2311 HInstruction* cls = MakeClassLoad();
2312 HInstruction* new_inst = MakeNewInstance(cls);
2313 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
2314 entry->AddInstruction(cls);
2315 entry->AddInstruction(new_inst);
2316 entry->AddInstruction(if_inst);
2317 ManuallyBuildEnvFor(cls, {});
2318 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
2319
2320 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
2321 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
2322 HInstruction* goto_left = new (GetAllocator()) HGoto();
2323 left->AddInstruction(call_left);
2324 left->AddInstruction(write_left);
2325 left->AddInstruction(goto_left);
2326 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
2327
2328 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
2329 HInstruction* goto_right = new (GetAllocator()) HGoto();
2330 right->AddInstruction(write_right);
2331 right->AddInstruction(goto_right);
2332
2333 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
2334 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
2335 breturn->AddInstruction(read_bottom);
2336 breturn->AddInstruction(return_exit);
2337
2338 SetupExit(exit);
2339
2340 // PerformLSE expects this to be empty.
2341 graph_->ClearDominanceInformation();
2342 PerformLSE();
2343
2344 EXPECT_INS_REMOVED(read_bottom);
2345 EXPECT_INS_REMOVED(write_right);
2346 EXPECT_INS_RETAINED(write_left);
2347 EXPECT_INS_RETAINED(call_left);
2348 }
2349
2350 template<typename Iter, typename Func>
FindOrNull(Iter begin,Iter end,Func func)2351 typename Iter::value_type FindOrNull(Iter begin, Iter end, Func func) {
2352 static_assert(std::is_pointer_v<typename Iter::value_type>);
2353 auto it = std::find_if(begin, end, func);
2354 if (it == end) {
2355 return nullptr;
2356 } else {
2357 return *it;
2358 }
2359 }
2360
2361 // // ENTRY
2362 // Obj new_inst = new Obj();
2363 // new_inst.foo = 12;
2364 // Obj obj;
2365 // Obj out;
2366 // int first;
2367 // if (param0) {
2368 // // ESCAPE_ROUTE
2369 // if (param1) {
2370 // // LEFT_START
2371 // if (param2) {
2372 // // LEFT_LEFT
2373 // obj = new_inst;
2374 // } else {
2375 // // LEFT_RIGHT
2376 // obj = obj_param;
2377 // }
2378 // // LEFT_MERGE
2379 // // technically the phi is enough to cause an escape but might as well be
2380 // // thorough.
2381 // // obj = phi[new_inst, param]
2382 // escape(obj);
2383 // out = obj;
2384 // } else {
2385 // // RIGHT
2386 // out = obj_param;
2387 // }
2388 // // EXIT
2389 // // Can't do anything with this since we don't have good tracking for the heap-locations
2390 // // out = phi[param, phi[new_inst, param]]
2391 // first = out.foo
2392 // } else {
2393 // new_inst.foo = 15;
2394 // first = 13;
2395 // }
2396 // // first = phi[out.foo, 13]
2397 // return first + new_inst.foo;
TEST_F(LoadStoreEliminationTest,PartialPhiPropagation)2398 TEST_F(LoadStoreEliminationTest, PartialPhiPropagation) {
2399 ScopedObjectAccess soa(Thread::Current());
2400 VariableSizedHandleScope vshs(soa.Self());
2401 CreateGraph(&vshs);
2402 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2403 "exit",
2404 {{"entry", "escape_route"},
2405 {"entry", "noescape_route"},
2406 {"escape_route", "left"},
2407 {"escape_route", "right"},
2408 {"left", "left_left"},
2409 {"left", "left_right"},
2410 {"left_left", "left_merge"},
2411 {"left_right", "left_merge"},
2412 {"left_merge", "escape_end"},
2413 {"right", "escape_end"},
2414 {"escape_end", "breturn"},
2415 {"noescape_route", "breturn"},
2416 {"breturn", "exit"}}));
2417 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
2418 GET_BLOCK(entry);
2419 GET_BLOCK(exit);
2420 GET_BLOCK(breturn);
2421 GET_BLOCK(left);
2422 GET_BLOCK(right);
2423 GET_BLOCK(left_left);
2424 GET_BLOCK(left_right);
2425 GET_BLOCK(left_merge);
2426 GET_BLOCK(escape_end);
2427 GET_BLOCK(escape_route);
2428 GET_BLOCK(noescape_route);
2429 #undef GET_BLOCK
2430 EnsurePredecessorOrder(escape_end, {left_merge, right});
2431 EnsurePredecessorOrder(left_merge, {left_left, left_right});
2432 EnsurePredecessorOrder(breturn, {escape_end, noescape_route});
2433 HInstruction* param0 = MakeParam(DataType::Type::kBool);
2434 HInstruction* param1 = MakeParam(DataType::Type::kBool);
2435 HInstruction* param2 = MakeParam(DataType::Type::kBool);
2436 HInstruction* obj_param = MakeParam(DataType::Type::kReference);
2437 HInstruction* c12 = graph_->GetIntConstant(12);
2438 HInstruction* c13 = graph_->GetIntConstant(13);
2439 HInstruction* c15 = graph_->GetIntConstant(15);
2440
2441 HInstruction* cls = MakeClassLoad();
2442 HInstruction* new_inst = MakeNewInstance(cls);
2443 HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32));
2444 HInstruction* if_param0 = new (GetAllocator()) HIf(param0);
2445 entry->AddInstruction(cls);
2446 entry->AddInstruction(new_inst);
2447 entry->AddInstruction(store);
2448 entry->AddInstruction(if_param0);
2449 ManuallyBuildEnvFor(cls, {});
2450 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
2451
2452 HInstruction* store_noescape = MakeIFieldSet(new_inst, c15, MemberOffset(32));
2453 noescape_route->AddInstruction(store_noescape);
2454 noescape_route->AddInstruction(new (GetAllocator()) HGoto());
2455
2456 escape_route->AddInstruction(new (GetAllocator()) HIf(param1));
2457
2458 HInstruction* if_left = new (GetAllocator()) HIf(param2);
2459 left->AddInstruction(if_left);
2460
2461 HInstruction* goto_left_left = new (GetAllocator()) HGoto();
2462 left_left->AddInstruction(goto_left_left);
2463
2464 HInstruction* goto_left_right = new (GetAllocator()) HGoto();
2465 left_right->AddInstruction(goto_left_right);
2466
2467 HPhi* left_phi = MakePhi({obj_param, new_inst});
2468 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { left_phi });
2469 HInstruction* goto_left_merge = new (GetAllocator()) HGoto();
2470 left_merge->AddPhi(left_phi);
2471 left_merge->AddInstruction(call_left);
2472 left_merge->AddInstruction(goto_left_merge);
2473 left_phi->SetCanBeNull(true);
2474 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
2475
2476 HInstruction* goto_right = new (GetAllocator()) HGoto();
2477 right->AddInstruction(goto_right);
2478
2479 HPhi* escape_end_phi = MakePhi({left_phi, obj_param});
2480 HInstruction* read_escape_end =
2481 MakeIFieldGet(escape_end_phi, DataType::Type::kInt32, MemberOffset(32));
2482 HInstruction* goto_escape_end = new (GetAllocator()) HGoto();
2483 escape_end->AddPhi(escape_end_phi);
2484 escape_end->AddInstruction(read_escape_end);
2485 escape_end->AddInstruction(goto_escape_end);
2486
2487 HPhi* return_phi = MakePhi({read_escape_end, c13});
2488 HInstruction* read_exit = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
2489 HInstruction* add_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, return_phi, read_exit);
2490 HInstruction* return_exit = new (GetAllocator()) HReturn(add_exit);
2491 breturn->AddPhi(return_phi);
2492 breturn->AddInstruction(read_exit);
2493 breturn->AddInstruction(add_exit);
2494 breturn->AddInstruction(return_exit);
2495
2496 SetupExit(exit);
2497
2498 // PerformLSE expects this to be empty.
2499 graph_->ClearDominanceInformation();
2500 LOG(INFO) << "Pre LSE " << blks;
2501 PerformLSEWithPartial();
2502 LOG(INFO) << "Post LSE " << blks;
2503
2504 HPredicatedInstanceFieldGet* pred_get =
2505 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_);
2506 std::vector<HPhi*> all_return_phis;
2507 std::tie(all_return_phis) = FindAllInstructions<HPhi>(graph_, breturn);
2508 EXPECT_EQ(all_return_phis.size(), 3u);
2509 EXPECT_INS_RETAINED(return_phi);
2510 EXPECT_TRUE(std::find(all_return_phis.begin(), all_return_phis.end(), return_phi) !=
2511 all_return_phis.end());
2512 HPhi* instance_phi =
2513 FindOrNull(all_return_phis.begin(), all_return_phis.end(), [&](HPhi* phi) {
2514 return phi != return_phi && phi->GetType() == DataType::Type::kReference;
2515 });
2516 ASSERT_NE(instance_phi, nullptr);
2517 HPhi* value_phi = FindOrNull(all_return_phis.begin(), all_return_phis.end(), [&](HPhi* phi) {
2518 return phi != return_phi && phi->GetType() == DataType::Type::kInt32;
2519 });
2520 ASSERT_NE(value_phi, nullptr);
2521 EXPECT_INS_EQ(
2522 instance_phi->InputAt(0),
2523 FindSingleInstruction<HNewInstance>(graph_, escape_route->GetSinglePredecessor()));
2524 // Check materialize block
2525 EXPECT_INS_EQ(FindSingleInstruction<HInstanceFieldSet>(
2526 graph_, escape_route->GetSinglePredecessor())
2527 ->InputAt(1),
2528 c12);
2529
2530 EXPECT_INS_EQ(instance_phi->InputAt(1), graph_->GetNullConstant());
2531 EXPECT_INS_EQ(value_phi->InputAt(0), graph_->GetIntConstant(0));
2532 EXPECT_INS_EQ(value_phi->InputAt(1), c15);
2533 EXPECT_INS_REMOVED(store_noescape);
2534 EXPECT_INS_EQ(pred_get->GetTarget(), instance_phi);
2535 EXPECT_INS_EQ(pred_get->GetDefaultValue(), value_phi);
2536 }
2537
2538 // // ENTRY
2539 // // To be moved
2540 // // NB Order important. By having alloc and store of obj1 before obj2 that
2541 // // ensure we'll build the materialization for obj1 first (just due to how
2542 // // we iterate.)
2543 // obj1 = new Obj();
2544 // obj2 = new Obj(); // has env[obj1]
2545 // // Swap the order of these
2546 // obj1.foo = param_obj1;
2547 // obj2.foo = param_obj2;
2548 // if (param1) {
2549 // // LEFT
2550 // obj2.foo = obj1;
2551 // if (param2) {
2552 // // LEFT_LEFT
2553 // escape(obj2);
2554 // } else {}
2555 // } else {}
2556 // return select(param3, obj1.foo, obj2.foo);
2557 // EXIT
TEST_P(OrderDependentTestGroup,PredicatedUse)2558 TEST_P(OrderDependentTestGroup, PredicatedUse) {
2559 ScopedObjectAccess soa(Thread::Current());
2560 VariableSizedHandleScope vshs(soa.Self());
2561 CreateGraph(&vshs);
2562 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2563 "exit",
2564 {{"entry", "left"},
2565 {"entry", "right"},
2566 {"left", "left_left"},
2567 {"left", "left_right"},
2568 {"left_left", "left_end"},
2569 {"left_right", "left_end"},
2570 {"left_end", "breturn"},
2571 {"right", "breturn"},
2572 {"breturn", "exit"}}));
2573 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
2574 GET_BLOCK(entry);
2575 GET_BLOCK(exit);
2576 GET_BLOCK(breturn);
2577 GET_BLOCK(right);
2578 GET_BLOCK(left);
2579 GET_BLOCK(left_left);
2580 GET_BLOCK(left_right);
2581 GET_BLOCK(left_end);
2582 #undef GET_BLOCK
2583 TestOrder order = GetParam();
2584 EnsurePredecessorOrder(breturn, {left_end, right});
2585 EnsurePredecessorOrder(left_end, {left_left, left_right});
2586 HInstruction* param1 = MakeParam(DataType::Type::kBool);
2587 HInstruction* param2 = MakeParam(DataType::Type::kBool);
2588 HInstruction* param3 = MakeParam(DataType::Type::kBool);
2589 HInstruction* param_obj1 = MakeParam(DataType::Type::kReference);
2590 HInstruction* param_obj2 = MakeParam(DataType::Type::kReference);
2591
2592 HInstruction* cls1 = MakeClassLoad();
2593 HInstruction* cls2 = MakeClassLoad();
2594 HInstruction* new_inst1 = MakeNewInstance(cls1);
2595 HInstruction* new_inst2 = MakeNewInstance(cls2);
2596 HInstruction* store1 = MakeIFieldSet(new_inst1, param_obj1, MemberOffset(32));
2597 HInstruction* store2 = MakeIFieldSet(new_inst2, param_obj2, MemberOffset(32));
2598 HInstruction* null_const = graph_->GetNullConstant();
2599 HInstruction* if_inst = new (GetAllocator()) HIf(param1);
2600 entry->AddInstruction(cls1);
2601 entry->AddInstruction(cls2);
2602 entry->AddInstruction(new_inst1);
2603 entry->AddInstruction(new_inst2);
2604 if (order == TestOrder::kSameAsAlloc) {
2605 entry->AddInstruction(store1);
2606 entry->AddInstruction(store2);
2607 } else {
2608 entry->AddInstruction(store2);
2609 entry->AddInstruction(store1);
2610 }
2611 entry->AddInstruction(if_inst);
2612 ManuallyBuildEnvFor(cls1, {});
2613 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
2614 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
2615 new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment());
2616
2617 // This is the escape of new_inst1
2618 HInstruction* store_left = MakeIFieldSet(new_inst2, new_inst1, MemberOffset(32));
2619 HInstruction* if_left = new (GetAllocator()) HIf(param2);
2620 left->AddInstruction(store_left);
2621 left->AddInstruction(if_left);
2622
2623 HInstruction* call_left_left = MakeInvoke(DataType::Type::kVoid, { new_inst2 });
2624 HInstruction* goto_left_left = new (GetAllocator()) HGoto();
2625 left_left->AddInstruction(call_left_left);
2626 left_left->AddInstruction(goto_left_left);
2627 call_left_left->CopyEnvironmentFrom(new_inst2->GetEnvironment());
2628
2629 left_right->AddInstruction(new (GetAllocator()) HGoto());
2630 left_end->AddInstruction(new (GetAllocator()) HGoto());
2631
2632 right->AddInstruction(new (GetAllocator()) HGoto());
2633
2634 // Used to distinguish the pred-gets without having to dig through the
2635 // multiple phi layers.
2636 constexpr uint32_t kRead1DexPc = 10;
2637 constexpr uint32_t kRead2DexPc = 20;
2638 HInstruction* read1 =
2639 MakeIFieldGet(new_inst1, DataType::Type::kReference, MemberOffset(32), kRead1DexPc);
2640 read1->SetReferenceTypeInfo(
2641 ReferenceTypeInfo::CreateUnchecked(graph_->GetHandleCache()->GetObjectClassHandle(), false));
2642 HInstruction* read2 =
2643 MakeIFieldGet(new_inst2, DataType::Type::kReference, MemberOffset(32), kRead2DexPc);
2644 read2->SetReferenceTypeInfo(
2645 ReferenceTypeInfo::CreateUnchecked(graph_->GetHandleCache()->GetObjectClassHandle(), false));
2646 HInstruction* sel_return = new (GetAllocator()) HSelect(param3, read1, read2, 0);
2647 HInstruction* return_exit = new (GetAllocator()) HReturn(sel_return);
2648 breturn->AddInstruction(read1);
2649 breturn->AddInstruction(read2);
2650 breturn->AddInstruction(sel_return);
2651 breturn->AddInstruction(return_exit);
2652
2653 SetupExit(exit);
2654
2655 // PerformLSE expects this to be empty.
2656 graph_->ClearDominanceInformation();
2657 LOG(INFO) << "Pre LSE " << blks;
2658 PerformLSEWithPartial();
2659 LOG(INFO) << "Post LSE " << blks;
2660
2661 EXPECT_INS_RETAINED(call_left_left);
2662 EXPECT_INS_REMOVED(read1);
2663 EXPECT_INS_REMOVED(read2);
2664 EXPECT_INS_REMOVED(new_inst1);
2665 EXPECT_INS_REMOVED(new_inst2);
2666 EXPECT_TRUE(new_inst1->GetUses().empty()) << *new_inst1 << " " << new_inst1->GetUses();
2667 EXPECT_TRUE(new_inst2->GetUses().empty()) << *new_inst2 << " " << new_inst2->GetUses();
2668 EXPECT_INS_RETAINED(sel_return);
2669 // Make sure the selector is the same
2670 EXPECT_INS_EQ(sel_return->InputAt(2), param3);
2671 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
2672 std::tie(pred_gets) = FindAllInstructions<HPredicatedInstanceFieldGet>(graph_, breturn);
2673 HPredicatedInstanceFieldGet* pred1 = FindOrNull(pred_gets.begin(), pred_gets.end(), [&](auto i) {
2674 return i->GetDexPc() == kRead1DexPc;
2675 });
2676 HPredicatedInstanceFieldGet* pred2 = FindOrNull(pred_gets.begin(), pred_gets.end(), [&](auto i) {
2677 return i->GetDexPc() == kRead2DexPc;
2678 });
2679 ASSERT_NE(pred1, nullptr);
2680 ASSERT_NE(pred2, nullptr);
2681 EXPECT_INS_EQ(sel_return->InputAt(0), pred2);
2682 EXPECT_INS_EQ(sel_return->InputAt(1), pred1);
2683 // Check targets
2684 EXPECT_TRUE(pred1->GetTarget()->IsPhi()) << pred1->DumpWithArgs();
2685 EXPECT_TRUE(pred2->GetTarget()->IsPhi()) << pred2->DumpWithArgs();
2686 HInstruction* mat1 = FindSingleInstruction<HNewInstance>(graph_, left->GetSinglePredecessor());
2687 HInstruction* mat2 =
2688 FindSingleInstruction<HNewInstance>(graph_, left_left->GetSinglePredecessor());
2689 EXPECT_INS_EQ(pred1->GetTarget()->InputAt(0), mat1);
2690 EXPECT_INS_EQ(pred1->GetTarget()->InputAt(1), null_const);
2691 EXPECT_TRUE(pred2->GetTarget()->InputAt(0)->IsPhi()) << pred2->DumpWithArgs();
2692 EXPECT_INS_EQ(pred2->GetTarget()->InputAt(0)->InputAt(0), mat2);
2693 EXPECT_INS_EQ(pred2->GetTarget()->InputAt(0)->InputAt(1), null_const);
2694 EXPECT_INS_EQ(pred2->GetTarget()->InputAt(1), null_const);
2695 // Check default values.
2696 EXPECT_TRUE(pred1->GetDefaultValue()->IsPhi()) << pred1->DumpWithArgs();
2697 EXPECT_TRUE(pred2->GetDefaultValue()->IsPhi()) << pred2->DumpWithArgs();
2698 EXPECT_INS_EQ(pred1->GetDefaultValue()->InputAt(0), null_const);
2699 EXPECT_INS_EQ(pred1->GetDefaultValue()->InputAt(1), param_obj1);
2700 EXPECT_TRUE(pred2->GetDefaultValue()->InputAt(0)->IsPhi()) << pred2->DumpWithArgs();
2701 EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(0)->InputAt(0), null_const);
2702 EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(0)->InputAt(1), mat1);
2703 EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(1), param_obj2);
2704 }
2705
2706 // // ENTRY
2707 // // To be moved
2708 // // NB Order important. By having alloc and store of obj1 before obj2 that
2709 // // ensure we'll build the materialization for obj1 first (just due to how
2710 // // we iterate.)
2711 // obj1 = new Obj();
2712 // obj.foo = 12;
2713 // obj2 = new Obj(); // has env[obj1]
2714 // obj2.foo = 15;
2715 // if (param1) {
2716 // // LEFT
2717 // // Need to update env to nullptr
2718 // escape(obj1/2);
2719 // if (param2) {
2720 // // LEFT_LEFT
2721 // escape(obj2/1);
2722 // } else {}
2723 // } else {}
2724 // return obj1.foo + obj2.foo;
2725 // EXIT
TEST_P(OrderDependentTestGroup,PredicatedEnvUse)2726 TEST_P(OrderDependentTestGroup, PredicatedEnvUse) {
2727 ScopedObjectAccess soa(Thread::Current());
2728 VariableSizedHandleScope vshs(soa.Self());
2729 CreateGraph(&vshs);
2730 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2731 "exit",
2732 {{"entry", "left"},
2733 {"entry", "right"},
2734 {"left", "left_left"},
2735 {"left", "left_right"},
2736 {"left_left", "left_end"},
2737 {"left_right", "left_end"},
2738 {"left_end", "breturn"},
2739 {"right", "breturn"},
2740 {"breturn", "exit"}}));
2741 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
2742 GET_BLOCK(entry);
2743 GET_BLOCK(exit);
2744 GET_BLOCK(breturn);
2745 GET_BLOCK(right);
2746 GET_BLOCK(left);
2747 GET_BLOCK(left_left);
2748 GET_BLOCK(left_right);
2749 GET_BLOCK(left_end);
2750 #undef GET_BLOCK
2751 TestOrder order = GetParam();
2752 EnsurePredecessorOrder(breturn, {left_end, right});
2753 EnsurePredecessorOrder(left_end, {left_left, left_right});
2754 HInstruction* param1 = MakeParam(DataType::Type::kBool);
2755 HInstruction* param2 = MakeParam(DataType::Type::kBool);
2756 HInstruction* c12 = graph_->GetIntConstant(12);
2757 HInstruction* c15 = graph_->GetIntConstant(15);
2758
2759 HInstruction* cls1 = MakeClassLoad();
2760 HInstruction* cls2 = MakeClassLoad();
2761 HInstruction* new_inst1 = MakeNewInstance(cls1);
2762 HInstruction* store1 = MakeIFieldSet(new_inst1, c12, MemberOffset(32));
2763 HInstruction* new_inst2 = MakeNewInstance(cls2);
2764 HInstruction* store2 = MakeIFieldSet(new_inst2, c15, MemberOffset(32));
2765 HInstruction* if_inst = new (GetAllocator()) HIf(param1);
2766 entry->AddInstruction(cls1);
2767 entry->AddInstruction(cls2);
2768 entry->AddInstruction(new_inst1);
2769 entry->AddInstruction(store1);
2770 entry->AddInstruction(new_inst2);
2771 entry->AddInstruction(store2);
2772 entry->AddInstruction(if_inst);
2773 ManuallyBuildEnvFor(cls1, {});
2774 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
2775 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
2776 ManuallyBuildEnvFor(new_inst2, {new_inst1});
2777
2778 HInstruction* first_inst = new_inst1;
2779 HInstruction* second_inst = new_inst2;
2780
2781 if (order == TestOrder::kReverseOfAlloc) {
2782 std::swap(first_inst, second_inst);
2783 }
2784
2785 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { first_inst });
2786 HInstruction* if_left = new (GetAllocator()) HIf(param2);
2787 left->AddInstruction(call_left);
2788 left->AddInstruction(if_left);
2789 call_left->CopyEnvironmentFrom(new_inst2->GetEnvironment());
2790
2791 HInstruction* call_left_left = MakeInvoke(DataType::Type::kVoid, { second_inst });
2792 HInstruction* goto_left_left = new (GetAllocator()) HGoto();
2793 left_left->AddInstruction(call_left_left);
2794 left_left->AddInstruction(goto_left_left);
2795 call_left_left->CopyEnvironmentFrom(new_inst2->GetEnvironment());
2796
2797 left_right->AddInstruction(new (GetAllocator()) HGoto());
2798 left_end->AddInstruction(new (GetAllocator()) HGoto());
2799
2800 right->AddInstruction(new (GetAllocator()) HGoto());
2801
2802 HInstruction* read1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32));
2803 HInstruction* read2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32));
2804 HInstruction* add_return = new (GetAllocator()) HAdd(DataType::Type::kInt32, read1, read2);
2805 HInstruction* return_exit = new (GetAllocator()) HReturn(add_return);
2806 breturn->AddInstruction(read1);
2807 breturn->AddInstruction(read2);
2808 breturn->AddInstruction(add_return);
2809 breturn->AddInstruction(return_exit);
2810
2811 SetupExit(exit);
2812
2813 // PerformLSE expects this to be empty.
2814 graph_->ClearDominanceInformation();
2815 LOG(INFO) << "Pre LSE " << blks;
2816 PerformLSEWithPartial();
2817 LOG(INFO) << "Post LSE " << blks;
2818
2819 HNewInstance* moved_new_inst1;
2820 HInstanceFieldSet* moved_set1;
2821 HNewInstance* moved_new_inst2;
2822 HInstanceFieldSet* moved_set2;
2823 HBasicBlock* first_mat_block = left->GetSinglePredecessor();
2824 HBasicBlock* second_mat_block = left_left->GetSinglePredecessor();
2825 if (order == TestOrder::kReverseOfAlloc) {
2826 std::swap(first_mat_block, second_mat_block);
2827 }
2828 std::tie(moved_new_inst1, moved_set1) =
2829 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, first_mat_block);
2830 std::tie(moved_new_inst2, moved_set2) =
2831 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, second_mat_block);
2832 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
2833 std::vector<HPhi*> phis;
2834 std::tie(pred_gets, phis) = FindAllInstructions<HPredicatedInstanceFieldGet, HPhi>(graph_);
2835 EXPECT_NE(moved_new_inst1, nullptr);
2836 EXPECT_NE(moved_new_inst2, nullptr);
2837 EXPECT_NE(moved_set1, nullptr);
2838 EXPECT_NE(moved_set2, nullptr);
2839 EXPECT_INS_EQ(moved_set1->InputAt(1), c12);
2840 EXPECT_INS_EQ(moved_set2->InputAt(1), c15);
2841 EXPECT_INS_RETAINED(call_left);
2842 EXPECT_INS_RETAINED(call_left_left);
2843 EXPECT_INS_REMOVED(store1);
2844 EXPECT_INS_REMOVED(store2);
2845 EXPECT_INS_REMOVED(read1);
2846 EXPECT_INS_REMOVED(read2);
2847 EXPECT_INS_EQ(moved_new_inst2->GetEnvironment()->GetInstructionAt(0),
2848 order == TestOrder::kSameAsAlloc
2849 ? moved_new_inst1
2850 : static_cast<HInstruction*>(graph_->GetNullConstant()));
2851 }
2852
2853 // // ENTRY
2854 // obj1 = new Obj1();
2855 // obj2 = new Obj2();
2856 // val1 = 3;
2857 // val2 = 13;
2858 // // The exact order the stores are written affects what the order we perform
2859 // // partial LSE on the values
2860 // obj1/2.field = val1/2;
2861 // obj2/1.field = val2/1;
2862 // if (parameter_value) {
2863 // // LEFT
2864 // escape(obj1);
2865 // escape(obj2);
2866 // } else {
2867 // // RIGHT
2868 // // ELIMINATE
2869 // obj1.field = 2;
2870 // obj2.field = 12;
2871 // }
2872 // EXIT
2873 // predicated-ELIMINATE
2874 // return obj1.field + obj2.field
TEST_P(OrderDependentTestGroup,FieldSetOrderEnv)2875 TEST_P(OrderDependentTestGroup, FieldSetOrderEnv) {
2876 ScopedObjectAccess soa(Thread::Current());
2877 VariableSizedHandleScope vshs(soa.Self());
2878 CreateGraph(/*handles=*/&vshs);
2879 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2880 "exit",
2881 {{"entry", "left"},
2882 {"entry", "right"},
2883 {"left", "breturn"},
2884 {"right", "breturn"},
2885 {"breturn", "exit"}}));
2886 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
2887 GET_BLOCK(entry);
2888 GET_BLOCK(exit);
2889 GET_BLOCK(breturn);
2890 GET_BLOCK(left);
2891 GET_BLOCK(right);
2892 #undef GET_BLOCK
2893 TestOrder order = GetParam();
2894 EnsurePredecessorOrder(breturn, {left, right});
2895 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
2896 HInstruction* c2 = graph_->GetIntConstant(2);
2897 HInstruction* c3 = graph_->GetIntConstant(3);
2898 HInstruction* c12 = graph_->GetIntConstant(12);
2899 HInstruction* c13 = graph_->GetIntConstant(13);
2900
2901 HInstruction* cls1 = MakeClassLoad();
2902 HInstruction* cls2 = MakeClassLoad();
2903 HInstruction* new_inst1 = MakeNewInstance(cls1);
2904 HInstruction* new_inst2 = MakeNewInstance(cls2);
2905 HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32));
2906 HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32));
2907 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
2908 entry->AddInstruction(cls1);
2909 entry->AddInstruction(cls2);
2910 entry->AddInstruction(new_inst1);
2911 entry->AddInstruction(new_inst2);
2912 if (order == TestOrder::kSameAsAlloc) {
2913 entry->AddInstruction(write_entry1);
2914 entry->AddInstruction(write_entry2);
2915 } else {
2916 entry->AddInstruction(write_entry2);
2917 entry->AddInstruction(write_entry1);
2918 }
2919 entry->AddInstruction(if_inst);
2920 ManuallyBuildEnvFor(cls1, {});
2921 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
2922 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
2923 ManuallyBuildEnvFor(new_inst2, {new_inst1});
2924
2925 HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 });
2926 HInstruction* call_left2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 });
2927 HInstruction* goto_left = new (GetAllocator()) HGoto();
2928 left->AddInstruction(call_left1);
2929 left->AddInstruction(call_left2);
2930 left->AddInstruction(goto_left);
2931 call_left1->CopyEnvironmentFrom(cls1->GetEnvironment());
2932 call_left2->CopyEnvironmentFrom(cls1->GetEnvironment());
2933
2934 HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32));
2935 HInstruction* write_right2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32));
2936 HInstruction* goto_right = new (GetAllocator()) HGoto();
2937 right->AddInstruction(write_right1);
2938 right->AddInstruction(write_right2);
2939 right->AddInstruction(goto_right);
2940
2941 HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32));
2942 HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32));
2943 HInstruction* combine =
2944 new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2);
2945 HInstruction* return_exit = new (GetAllocator()) HReturn(combine);
2946 breturn->AddInstruction(read_bottom1);
2947 breturn->AddInstruction(read_bottom2);
2948 breturn->AddInstruction(combine);
2949 breturn->AddInstruction(return_exit);
2950
2951 SetupExit(exit);
2952
2953 // PerformLSE expects this to be empty.
2954 graph_->ClearDominanceInformation();
2955 LOG(INFO) << "Pre LSE " << blks;
2956 PerformLSEWithPartial();
2957 LOG(INFO) << "Post LSE " << blks;
2958
2959 EXPECT_INS_REMOVED(write_entry1);
2960 EXPECT_INS_REMOVED(write_entry2);
2961 EXPECT_INS_REMOVED(read_bottom1);
2962 EXPECT_INS_REMOVED(read_bottom2);
2963 EXPECT_INS_REMOVED(write_right1);
2964 EXPECT_INS_REMOVED(write_right2);
2965 EXPECT_INS_RETAINED(call_left1);
2966 EXPECT_INS_RETAINED(call_left2);
2967 std::vector<HPhi*> merges;
2968 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
2969 std::vector<HNewInstance*> materializations;
2970 std::tie(merges, pred_gets) =
2971 FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn);
2972 std::tie(materializations) = FindAllInstructions<HNewInstance>(graph_);
2973 ASSERT_EQ(merges.size(), 4u);
2974 ASSERT_EQ(pred_gets.size(), 2u);
2975 ASSERT_EQ(materializations.size(), 2u);
2976 HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
2977 return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2;
2978 });
2979 HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
2980 return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c12;
2981 });
2982 HNewInstance* mat_alloc1 = FindOrNull(materializations.begin(),
2983 materializations.end(),
2984 [&](HNewInstance* n) { return n->InputAt(0) == cls1; });
2985 HNewInstance* mat_alloc2 = FindOrNull(materializations.begin(),
2986 materializations.end(),
2987 [&](HNewInstance* n) { return n->InputAt(0) == cls2; });
2988 ASSERT_NE(mat_alloc1, nullptr);
2989 ASSERT_NE(mat_alloc2, nullptr);
2990 HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
2991 return p->GetType() == DataType::Type::kReference && p->InputAt(0) == mat_alloc1;
2992 });
2993 HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
2994 return p->GetType() == DataType::Type::kReference && p->InputAt(0) == mat_alloc2;
2995 });
2996 ASSERT_NE(merge_alloc1, nullptr);
2997 HPredicatedInstanceFieldGet* pred_get1 =
2998 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
2999 return pg->GetTarget() == merge_alloc1;
3000 });
3001 ASSERT_NE(merge_alloc2, nullptr);
3002 HPredicatedInstanceFieldGet* pred_get2 =
3003 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
3004 return pg->GetTarget() == merge_alloc2;
3005 });
3006 ASSERT_NE(merge_value_return1, nullptr);
3007 ASSERT_NE(merge_value_return2, nullptr);
3008 EXPECT_INS_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant());
3009 EXPECT_INS_EQ(merge_alloc2->InputAt(1), graph_->GetNullConstant());
3010 ASSERT_NE(pred_get1, nullptr);
3011 EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1);
3012 EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1)
3013 << " pred-get is: " << *pred_get1;
3014 EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0))
3015 << " merge val is: " << *merge_value_return1;
3016 EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1;
3017 ASSERT_NE(pred_get2, nullptr);
3018 EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2);
3019 EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2)
3020 << " pred-get is: " << *pred_get2;
3021 EXPECT_INS_EQ(merge_value_return2->InputAt(0), graph_->GetIntConstant(0))
3022 << " merge val is: " << *merge_value_return1;
3023 EXPECT_INS_EQ(merge_value_return2->InputAt(1), c12) << " merge val is: " << *merge_value_return1;
3024 EXPECT_INS_EQ(mat_alloc2->GetEnvironment()->GetInstructionAt(0), mat_alloc1);
3025 }
3026
3027 // // TODO We can compile this better if we are better able to understand lifetimes.
3028 // // ENTRY
3029 // obj1 = new Obj1();
3030 // obj2 = new Obj2();
3031 // // The exact order the stores are written affects what the order we perform
3032 // // partial LSE on the values
3033 // obj{1,2}.var = param_obj;
3034 // obj{2,1}.var = param_obj;
3035 // if (param_1) {
3036 // // EARLY_RETURN
3037 // return;
3038 // }
3039 // // escape of obj1
3040 // obj2.var = obj1;
3041 // if (param_2) {
3042 // // escape of obj2 with a materialization that uses obj1
3043 // escape(obj2);
3044 // }
3045 // // EXIT
3046 // return;
TEST_P(OrderDependentTestGroup,MaterializationMovedUse)3047 TEST_P(OrderDependentTestGroup, MaterializationMovedUse) {
3048 ScopedObjectAccess soa(Thread::Current());
3049 VariableSizedHandleScope vshs(soa.Self());
3050 CreateGraph(/*handles=*/&vshs);
3051 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3052 "exit",
3053 {{"entry", "early_return"},
3054 {"early_return", "exit"},
3055 {"entry", "escape_1"},
3056 {"escape_1", "escape_2"},
3057 {"escape_1", "escape_1_crit_break"},
3058 {"escape_1_crit_break", "exit"},
3059 {"escape_2", "exit"}}));
3060 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3061 GET_BLOCK(entry);
3062 GET_BLOCK(exit);
3063 GET_BLOCK(early_return);
3064 GET_BLOCK(escape_1);
3065 GET_BLOCK(escape_1_crit_break);
3066 GET_BLOCK(escape_2);
3067 #undef GET_BLOCK
3068 TestOrder order = GetParam();
3069 HInstruction* param_1 = MakeParam(DataType::Type::kBool);
3070 HInstruction* param_2 = MakeParam(DataType::Type::kBool);
3071 HInstruction* param_obj = MakeParam(DataType::Type::kReference);
3072
3073 HInstruction* cls1 = MakeClassLoad();
3074 HInstruction* cls2 = MakeClassLoad();
3075 HInstruction* new_inst1 = MakeNewInstance(cls1);
3076 HInstruction* new_inst2 = MakeNewInstance(cls2);
3077 HInstruction* write_entry1 = MakeIFieldSet(new_inst1, param_obj, MemberOffset(32));
3078 HInstruction* write_entry2 = MakeIFieldSet(new_inst2, param_obj, MemberOffset(32));
3079 HInstruction* if_inst = new (GetAllocator()) HIf(param_1);
3080 entry->AddInstruction(cls1);
3081 entry->AddInstruction(cls2);
3082 entry->AddInstruction(new_inst1);
3083 entry->AddInstruction(new_inst2);
3084 if (order == TestOrder::kSameAsAlloc) {
3085 entry->AddInstruction(write_entry1);
3086 entry->AddInstruction(write_entry2);
3087 } else {
3088 entry->AddInstruction(write_entry2);
3089 entry->AddInstruction(write_entry1);
3090 }
3091 entry->AddInstruction(if_inst);
3092 ManuallyBuildEnvFor(cls1, {});
3093 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
3094 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
3095 new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment());
3096
3097 early_return->AddInstruction(new (GetAllocator()) HReturnVoid());
3098
3099 HInstruction* escape_1_set = MakeIFieldSet(new_inst2, new_inst1, MemberOffset(32));
3100 HInstruction* escape_1_if = new (GetAllocator()) HIf(param_2);
3101 escape_1->AddInstruction(escape_1_set);
3102 escape_1->AddInstruction(escape_1_if);
3103
3104 escape_1_crit_break->AddInstruction(new (GetAllocator()) HReturnVoid());
3105
3106 HInstruction* escape_2_call = MakeInvoke(DataType::Type::kVoid, {new_inst2});
3107 HInstruction* escape_2_return = new (GetAllocator()) HReturnVoid();
3108 escape_2->AddInstruction(escape_2_call);
3109 escape_2->AddInstruction(escape_2_return);
3110 escape_2_call->CopyEnvironmentFrom(cls1->GetEnvironment());
3111
3112 SetupExit(exit);
3113
3114 // PerformLSE expects this to be empty.
3115 graph_->ClearDominanceInformation();
3116 LOG(INFO) << "Pre LSE " << blks;
3117 PerformLSEWithPartial();
3118 LOG(INFO) << "Post LSE " << blks;
3119
3120 EXPECT_INS_REMOVED(new_inst1);
3121 EXPECT_INS_REMOVED(new_inst2);
3122 EXPECT_INS_REMOVED(write_entry1);
3123 EXPECT_INS_REMOVED(write_entry2);
3124 EXPECT_INS_REMOVED(escape_1_set);
3125 EXPECT_INS_RETAINED(escape_2_call);
3126
3127 HInstruction* obj1_mat =
3128 FindSingleInstruction<HNewInstance>(graph_, escape_1->GetSinglePredecessor());
3129 HInstruction* obj1_set =
3130 FindSingleInstruction<HInstanceFieldSet>(graph_, escape_1->GetSinglePredecessor());
3131 HInstruction* obj2_mat =
3132 FindSingleInstruction<HNewInstance>(graph_, escape_2->GetSinglePredecessor());
3133 HInstruction* obj2_set =
3134 FindSingleInstruction<HInstanceFieldSet>(graph_, escape_2->GetSinglePredecessor());
3135 ASSERT_TRUE(obj1_mat != nullptr);
3136 ASSERT_TRUE(obj2_mat != nullptr);
3137 ASSERT_TRUE(obj1_set != nullptr);
3138 ASSERT_TRUE(obj2_set != nullptr);
3139 EXPECT_INS_EQ(obj1_set->InputAt(0), obj1_mat);
3140 EXPECT_INS_EQ(obj1_set->InputAt(1), param_obj);
3141 EXPECT_INS_EQ(obj2_set->InputAt(0), obj2_mat);
3142 EXPECT_INS_EQ(obj2_set->InputAt(1), obj1_mat);
3143 }
3144
3145 INSTANTIATE_TEST_SUITE_P(LoadStoreEliminationTest,
3146 OrderDependentTestGroup,
3147 testing::Values(TestOrder::kSameAsAlloc, TestOrder::kReverseOfAlloc));
3148
3149 // // ENTRY
3150 // // To be moved
3151 // obj = new Obj();
3152 // obj.foo = 12;
3153 // if (parameter_value) {
3154 // // LEFT
3155 // escape(obj);
3156 // } else {}
3157 // EXIT
TEST_F(LoadStoreEliminationTest,MovePredicatedAlloc)3158 TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc) {
3159 ScopedObjectAccess soa(Thread::Current());
3160 VariableSizedHandleScope vshs(soa.Self());
3161 CreateGraph(&vshs);
3162 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3163 "exit",
3164 {{"entry", "left"},
3165 {"entry", "right"},
3166 {"right", "breturn"},
3167 {"left", "breturn"},
3168 {"breturn", "exit"}}));
3169 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3170 GET_BLOCK(entry);
3171 GET_BLOCK(exit);
3172 GET_BLOCK(breturn);
3173 GET_BLOCK(left);
3174 GET_BLOCK(right);
3175 #undef GET_BLOCK
3176 EnsurePredecessorOrder(breturn, {left, right});
3177 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
3178 HInstruction* c12 = graph_->GetIntConstant(12);
3179
3180 HInstruction* cls = MakeClassLoad();
3181 HInstruction* new_inst = MakeNewInstance(cls);
3182 HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32));
3183 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
3184 entry->AddInstruction(cls);
3185 entry->AddInstruction(new_inst);
3186 entry->AddInstruction(store);
3187 entry->AddInstruction(if_inst);
3188 ManuallyBuildEnvFor(cls, {});
3189 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3190
3191 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
3192 HInstruction* goto_left = new (GetAllocator()) HGoto();
3193 left->AddInstruction(call_left);
3194 left->AddInstruction(goto_left);
3195 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
3196
3197 right->AddInstruction(new (GetAllocator()) HGoto());
3198
3199 HInstruction* return_exit = new (GetAllocator()) HReturnVoid();
3200 breturn->AddInstruction(return_exit);
3201
3202 SetupExit(exit);
3203
3204 // PerformLSE expects this to be empty.
3205 graph_->ClearDominanceInformation();
3206 LOG(INFO) << "Pre LSE " << blks;
3207 PerformLSEWithPartial();
3208 LOG(INFO) << "Post LSE " << blks;
3209
3210 HNewInstance* moved_new_inst = nullptr;
3211 HInstanceFieldSet* moved_set = nullptr;
3212 std::tie(moved_new_inst, moved_set) =
3213 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_);
3214 EXPECT_NE(moved_new_inst, nullptr);
3215 EXPECT_NE(moved_set, nullptr);
3216 EXPECT_INS_RETAINED(call_left);
3217 // store removed or moved.
3218 EXPECT_NE(store->GetBlock(), entry);
3219 // New-inst removed or moved.
3220 EXPECT_NE(new_inst->GetBlock(), entry);
3221 EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst);
3222 EXPECT_INS_EQ(moved_set->InputAt(1), c12);
3223 }
3224
3225 // // ENTRY
3226 // // To be moved
3227 // obj = new Obj();
3228 // obj.foo = 12;
3229 // if (parameter_value) {
3230 // // LEFT
3231 // escape(obj);
3232 // }
3233 // EXIT
3234 // int a = obj.foo;
3235 // obj.foo = 13;
3236 // noescape();
3237 // int b = obj.foo;
3238 // obj.foo = 14;
3239 // noescape();
3240 // int c = obj.foo;
3241 // obj.foo = 15;
3242 // noescape();
3243 // return a + b + c
TEST_F(LoadStoreEliminationTest,MutiPartialLoadStore)3244 TEST_F(LoadStoreEliminationTest, MutiPartialLoadStore) {
3245 ScopedObjectAccess soa(Thread::Current());
3246 VariableSizedHandleScope vshs(soa.Self());
3247 CreateGraph(&vshs);
3248 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3249 "exit",
3250 {{"entry", "left"},
3251 {"entry", "right"},
3252 {"right", "breturn"},
3253 {"left", "breturn"},
3254 {"breturn", "exit"}}));
3255 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3256 GET_BLOCK(entry);
3257 GET_BLOCK(exit);
3258 GET_BLOCK(breturn);
3259 GET_BLOCK(left);
3260 GET_BLOCK(right);
3261 #undef GET_BLOCK
3262 EnsurePredecessorOrder(breturn, {left, right});
3263 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
3264 HInstruction* c12 = graph_->GetIntConstant(12);
3265 HInstruction* c13 = graph_->GetIntConstant(13);
3266 HInstruction* c14 = graph_->GetIntConstant(14);
3267 HInstruction* c15 = graph_->GetIntConstant(15);
3268
3269 HInstruction* cls = MakeClassLoad();
3270 HInstruction* new_inst = MakeNewInstance(cls);
3271 HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32));
3272 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
3273 entry->AddInstruction(cls);
3274 entry->AddInstruction(new_inst);
3275 entry->AddInstruction(store);
3276 entry->AddInstruction(if_inst);
3277 ManuallyBuildEnvFor(cls, {});
3278 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3279
3280 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
3281 HInstruction* goto_left = new (GetAllocator()) HGoto();
3282 left->AddInstruction(call_left);
3283 left->AddInstruction(goto_left);
3284 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
3285
3286 HInstruction* goto_right = new (GetAllocator()) HGoto();
3287 right->AddInstruction(goto_right);
3288
3289 HInstruction* a_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3290 HInstruction* a_reset = MakeIFieldSet(new_inst, c13, MemberOffset(32));
3291 HInstruction* a_noescape = MakeInvoke(DataType::Type::kVoid, {});
3292 HInstruction* b_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3293 HInstruction* b_reset = MakeIFieldSet(new_inst, c14, MemberOffset(32));
3294 HInstruction* b_noescape = MakeInvoke(DataType::Type::kVoid, {});
3295 HInstruction* c_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3296 HInstruction* c_reset = MakeIFieldSet(new_inst, c15, MemberOffset(32));
3297 HInstruction* c_noescape = MakeInvoke(DataType::Type::kVoid, {});
3298 HInstruction* add_1_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, a_val, b_val);
3299 HInstruction* add_2_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, c_val, add_1_exit);
3300 HInstruction* return_exit = new (GetAllocator()) HReturn(add_2_exit);
3301 breturn->AddInstruction(a_val);
3302 breturn->AddInstruction(a_reset);
3303 breturn->AddInstruction(a_noescape);
3304 breturn->AddInstruction(b_val);
3305 breturn->AddInstruction(b_reset);
3306 breturn->AddInstruction(b_noescape);
3307 breturn->AddInstruction(c_val);
3308 breturn->AddInstruction(c_reset);
3309 breturn->AddInstruction(c_noescape);
3310 breturn->AddInstruction(add_1_exit);
3311 breturn->AddInstruction(add_2_exit);
3312 breturn->AddInstruction(return_exit);
3313 ManuallyBuildEnvFor(a_noescape, {new_inst, a_val});
3314 ManuallyBuildEnvFor(b_noescape, {new_inst, a_val, b_val});
3315 ManuallyBuildEnvFor(c_noescape, {new_inst, a_val, b_val, c_val});
3316
3317 SetupExit(exit);
3318
3319 // PerformLSE expects this to be empty.
3320 graph_->ClearDominanceInformation();
3321 LOG(INFO) << "Pre LSE " << blks;
3322 PerformLSEWithPartial();
3323 LOG(INFO) << "Post LSE " << blks;
3324
3325 HNewInstance* moved_new_inst = nullptr;
3326 HInstanceFieldSet* moved_set = nullptr;
3327 std::tie(moved_new_inst, moved_set) =
3328 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, left->GetSinglePredecessor());
3329 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
3330 std::vector<HInstanceFieldSet*> pred_sets;
3331 std::vector<HPhi*> return_phis;
3332 std::tie(return_phis, pred_gets, pred_sets) =
3333 FindAllInstructions<HPhi, HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_, breturn);
3334 ASSERT_EQ(return_phis.size(), 2u);
3335 HPhi* inst_phi = return_phis[0];
3336 HPhi* val_phi = return_phis[1];
3337 if (inst_phi->GetType() != DataType::Type::kReference) {
3338 std::swap(inst_phi, val_phi);
3339 }
3340 ASSERT_NE(moved_new_inst, nullptr);
3341 EXPECT_INS_EQ(inst_phi->InputAt(0), moved_new_inst);
3342 EXPECT_INS_EQ(inst_phi->InputAt(1), graph_->GetNullConstant());
3343 EXPECT_INS_EQ(val_phi->InputAt(0), graph_->GetIntConstant(0));
3344 EXPECT_EQ(val_phi->InputAt(1), c12);
3345 ASSERT_EQ(pred_gets.size(), 3u);
3346 ASSERT_EQ(pred_gets.size(), pred_sets.size());
3347 std::vector<HInstruction*> set_values{c13, c14, c15};
3348 std::vector<HInstruction*> get_values{val_phi, c13, c14};
3349 ASSERT_NE(moved_set, nullptr);
3350 EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst);
3351 EXPECT_INS_EQ(moved_set->InputAt(1), c12);
3352 EXPECT_INS_RETAINED(call_left);
3353 // store removed or moved.
3354 EXPECT_NE(store->GetBlock(), entry);
3355 // New-inst removed or moved.
3356 EXPECT_NE(new_inst->GetBlock(), entry);
3357 for (auto [get, val] : ZipLeft(MakeIterationRange(pred_gets), MakeIterationRange(get_values))) {
3358 EXPECT_INS_EQ(get->GetDefaultValue(), val);
3359 }
3360 for (auto [set, val] : ZipLeft(MakeIterationRange(pred_sets), MakeIterationRange(set_values))) {
3361 EXPECT_INS_EQ(set->InputAt(1), val);
3362 EXPECT_TRUE(set->GetIsPredicatedSet()) << *set;
3363 }
3364 EXPECT_INS_RETAINED(a_noescape);
3365 EXPECT_INS_RETAINED(b_noescape);
3366 EXPECT_INS_RETAINED(c_noescape);
3367 EXPECT_INS_EQ(add_1_exit->InputAt(0), pred_gets[0]);
3368 EXPECT_INS_EQ(add_1_exit->InputAt(1), pred_gets[1]);
3369 EXPECT_INS_EQ(add_2_exit->InputAt(0), pred_gets[2]);
3370
3371 EXPECT_EQ(a_noescape->GetEnvironment()->Size(), 2u);
3372 EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi);
3373 EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]);
3374 EXPECT_EQ(b_noescape->GetEnvironment()->Size(), 3u);
3375 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi);
3376 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]);
3377 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(2), pred_gets[1]);
3378 EXPECT_EQ(c_noescape->GetEnvironment()->Size(), 4u);
3379 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi);
3380 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]);
3381 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(2), pred_gets[1]);
3382 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(3), pred_gets[2]);
3383 }
3384
3385 // // ENTRY
3386 // // To be moved
3387 // obj = new Obj();
3388 // obj.foo = 12;
3389 // int a = obj.foo;
3390 // obj.foo = 13;
3391 // noescape();
3392 // int b = obj.foo;
3393 // obj.foo = 14;
3394 // noescape();
3395 // int c = obj.foo;
3396 // obj.foo = 15;
3397 // noescape();
3398 // if (parameter_value) {
3399 // // LEFT
3400 // escape(obj);
3401 // }
3402 // EXIT
3403 // return a + b + c + obj.foo
TEST_F(LoadStoreEliminationTest,MutiPartialLoadStore2)3404 TEST_F(LoadStoreEliminationTest, MutiPartialLoadStore2) {
3405 ScopedObjectAccess soa(Thread::Current());
3406 VariableSizedHandleScope vshs(soa.Self());
3407 CreateGraph(&vshs);
3408 // Need to have an actual entry block since we check env-layout and the way we
3409 // add constants would screw this up otherwise.
3410 AdjacencyListGraph blks(SetupFromAdjacencyList("start",
3411 "exit",
3412 {{"start", "entry"},
3413 {"entry", "left"},
3414 {"entry", "right"},
3415 {"right", "breturn"},
3416 {"left", "breturn"},
3417 {"breturn", "exit"}}));
3418 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3419 GET_BLOCK(start);
3420 GET_BLOCK(entry);
3421 GET_BLOCK(exit);
3422 GET_BLOCK(breturn);
3423 GET_BLOCK(left);
3424 GET_BLOCK(right);
3425 #undef GET_BLOCK
3426 EnsurePredecessorOrder(breturn, {left, right});
3427 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
3428 HInstruction* c12 = graph_->GetIntConstant(12);
3429 HInstruction* c13 = graph_->GetIntConstant(13);
3430 HInstruction* c14 = graph_->GetIntConstant(14);
3431 HInstruction* c15 = graph_->GetIntConstant(15);
3432
3433 HInstruction* start_suspend = new (GetAllocator()) HSuspendCheck();
3434 HInstruction* start_goto = new (GetAllocator()) HGoto();
3435
3436 start->AddInstruction(start_suspend);
3437 start->AddInstruction(start_goto);
3438 ManuallyBuildEnvFor(start_suspend, {});
3439
3440 HInstruction* cls = MakeClassLoad();
3441 HInstruction* new_inst = MakeNewInstance(cls);
3442 HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32));
3443
3444 HInstruction* a_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3445 HInstruction* a_reset = MakeIFieldSet(new_inst, c13, MemberOffset(32));
3446 HInstruction* a_noescape = MakeInvoke(DataType::Type::kVoid, {});
3447 HInstruction* b_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3448 HInstruction* b_reset = MakeIFieldSet(new_inst, c14, MemberOffset(32));
3449 HInstruction* b_noescape = MakeInvoke(DataType::Type::kVoid, {});
3450 HInstruction* c_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3451 HInstruction* c_reset = MakeIFieldSet(new_inst, c15, MemberOffset(32));
3452 HInstruction* c_noescape = MakeInvoke(DataType::Type::kVoid, {});
3453 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
3454 entry->AddInstruction(cls);
3455 entry->AddInstruction(new_inst);
3456 entry->AddInstruction(store);
3457 entry->AddInstruction(a_val);
3458 entry->AddInstruction(a_reset);
3459 entry->AddInstruction(a_noescape);
3460 entry->AddInstruction(b_val);
3461 entry->AddInstruction(b_reset);
3462 entry->AddInstruction(b_noescape);
3463 entry->AddInstruction(c_val);
3464 entry->AddInstruction(c_reset);
3465 entry->AddInstruction(c_noescape);
3466 entry->AddInstruction(if_inst);
3467 ManuallyBuildEnvFor(cls, {});
3468 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3469 ManuallyBuildEnvFor(a_noescape, {new_inst, a_val});
3470 ManuallyBuildEnvFor(b_noescape, {new_inst, a_val, b_val});
3471 ManuallyBuildEnvFor(c_noescape, {new_inst, a_val, b_val, c_val});
3472
3473 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
3474 HInstruction* goto_left = new (GetAllocator()) HGoto();
3475 left->AddInstruction(call_left);
3476 left->AddInstruction(goto_left);
3477 call_left->CopyEnvironmentFrom(c_noescape->GetEnvironment());
3478
3479 HInstruction* goto_right = new (GetAllocator()) HGoto();
3480 right->AddInstruction(goto_right);
3481
3482 HInstruction* val_exit = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3483 HInstruction* add_1_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, a_val, b_val);
3484 HInstruction* add_2_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, c_val, add_1_exit);
3485 HInstruction* add_3_exit =
3486 new (GetAllocator()) HAdd(DataType::Type::kInt32, val_exit, add_2_exit);
3487 HInstruction* return_exit = new (GetAllocator()) HReturn(add_3_exit);
3488 breturn->AddInstruction(val_exit);
3489 breturn->AddInstruction(add_1_exit);
3490 breturn->AddInstruction(add_2_exit);
3491 breturn->AddInstruction(add_3_exit);
3492 breturn->AddInstruction(return_exit);
3493
3494 SetupExit(exit);
3495
3496 // PerformLSE expects this to be empty.
3497 graph_->ClearDominanceInformation();
3498 LOG(INFO) << "Pre LSE " << blks;
3499 PerformLSEWithPartial();
3500 LOG(INFO) << "Post LSE " << blks;
3501
3502 HNewInstance* moved_new_inst = nullptr;
3503 HInstanceFieldSet* moved_set = nullptr;
3504 std::tie(moved_new_inst, moved_set) =
3505 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, left->GetSinglePredecessor());
3506 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
3507 std::vector<HInstanceFieldSet*> pred_sets;
3508 std::vector<HPhi*> return_phis;
3509 std::tie(return_phis, pred_gets, pred_sets) =
3510 FindAllInstructions<HPhi, HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_, breturn);
3511 ASSERT_EQ(return_phis.size(), 2u);
3512 HPhi* inst_phi = return_phis[0];
3513 HPhi* val_phi = return_phis[1];
3514 if (inst_phi->GetType() != DataType::Type::kReference) {
3515 std::swap(inst_phi, val_phi);
3516 }
3517 ASSERT_NE(moved_new_inst, nullptr);
3518 EXPECT_INS_EQ(inst_phi->InputAt(0), moved_new_inst);
3519 EXPECT_INS_EQ(inst_phi->InputAt(1), graph_->GetNullConstant());
3520 EXPECT_INS_EQ(val_phi->InputAt(0), graph_->GetIntConstant(0));
3521 EXPECT_INS_EQ(val_phi->InputAt(1), c15);
3522 ASSERT_EQ(pred_gets.size(), 1u);
3523 ASSERT_EQ(pred_sets.size(), 0u);
3524 ASSERT_NE(moved_set, nullptr);
3525 EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst);
3526 EXPECT_INS_EQ(moved_set->InputAt(1), c15);
3527 EXPECT_INS_RETAINED(call_left);
3528 // store removed or moved.
3529 EXPECT_NE(store->GetBlock(), entry);
3530 // New-inst removed or moved.
3531 EXPECT_NE(new_inst->GetBlock(), entry);
3532 EXPECT_INS_REMOVED(a_val);
3533 EXPECT_INS_REMOVED(b_val);
3534 EXPECT_INS_REMOVED(c_val);
3535 EXPECT_INS_RETAINED(a_noescape);
3536 EXPECT_INS_RETAINED(b_noescape);
3537 EXPECT_INS_RETAINED(c_noescape);
3538 EXPECT_INS_EQ(add_1_exit->InputAt(0), c12);
3539 EXPECT_INS_EQ(add_1_exit->InputAt(1), c13);
3540 EXPECT_INS_EQ(add_2_exit->InputAt(0), c14);
3541 EXPECT_INS_EQ(add_2_exit->InputAt(1), add_1_exit);
3542 EXPECT_INS_EQ(add_3_exit->InputAt(0), pred_gets[0]);
3543 EXPECT_INS_EQ(pred_gets[0]->GetDefaultValue(), val_phi);
3544 EXPECT_INS_EQ(add_3_exit->InputAt(1), add_2_exit);
3545 EXPECT_EQ(a_noescape->GetEnvironment()->Size(), 2u);
3546 EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant());
3547 EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(1), c12);
3548 EXPECT_EQ(b_noescape->GetEnvironment()->Size(), 3u);
3549 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant());
3550 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(1), c12);
3551 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(2), c13);
3552 EXPECT_EQ(c_noescape->GetEnvironment()->Size(), 4u);
3553 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant());
3554 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(1), c12);
3555 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(2), c13);
3556 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(3), c14);
3557 }
3558
3559 // // ENTRY
3560 // // To be moved
3561 // obj = new Obj();
3562 // // Transforms required for creation non-trivial and unimportant
3563 // if (parameter_value) {
3564 // obj.foo = 10
3565 // } else {
3566 // obj.foo = 12;
3567 // }
3568 // if (parameter_value_2) {
3569 // escape(obj);
3570 // }
3571 // EXIT
TEST_F(LoadStoreEliminationTest,MovePredicatedAlloc2)3572 TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc2) {
3573 ScopedObjectAccess soa(Thread::Current());
3574 VariableSizedHandleScope vshs(soa.Self());
3575 CreateGraph(&vshs);
3576 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3577 "exit",
3578 {{"entry", "left_set"},
3579 {"entry", "right_set"},
3580 {"left_set", "merge_crit_break"},
3581 {"right_set", "merge_crit_break"},
3582 {"merge_crit_break", "merge"},
3583 {"merge", "escape"},
3584 {"escape", "breturn"},
3585 {"merge", "breturn"},
3586 {"breturn", "exit"}}));
3587 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3588 GET_BLOCK(entry);
3589 GET_BLOCK(exit);
3590 GET_BLOCK(breturn);
3591 GET_BLOCK(left_set);
3592 GET_BLOCK(right_set);
3593 GET_BLOCK(merge);
3594 GET_BLOCK(merge_crit_break);
3595 GET_BLOCK(escape);
3596 #undef GET_BLOCK
3597 EnsurePredecessorOrder(breturn, {merge, escape});
3598 EnsurePredecessorOrder(merge_crit_break, {left_set, right_set});
3599 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
3600 HInstruction* bool_value_2 = MakeParam(DataType::Type::kBool);
3601 HInstruction* c10 = graph_->GetIntConstant(10);
3602 HInstruction* c12 = graph_->GetIntConstant(12);
3603
3604 HInstruction* cls = MakeClassLoad();
3605 HInstruction* new_inst = MakeNewInstance(cls);
3606 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
3607 entry->AddInstruction(cls);
3608 entry->AddInstruction(new_inst);
3609 entry->AddInstruction(if_inst);
3610 ManuallyBuildEnvFor(cls, {});
3611 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3612
3613 HInstruction* store_left = MakeIFieldSet(new_inst, c10, MemberOffset(32));
3614 HInstruction* goto_left = new (GetAllocator()) HGoto();
3615 left_set->AddInstruction(store_left);
3616 left_set->AddInstruction(goto_left);
3617
3618 HInstruction* store_right = MakeIFieldSet(new_inst, c12, MemberOffset(32));
3619 HInstruction* goto_right = new (GetAllocator()) HGoto();
3620 right_set->AddInstruction(store_right);
3621 right_set->AddInstruction(goto_right);
3622
3623 merge_crit_break->AddInstruction(new (GetAllocator()) HGoto());
3624 HInstruction* if_merge = new (GetAllocator()) HIf(bool_value_2);
3625 merge->AddInstruction(if_merge);
3626
3627 HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst });
3628 HInstruction* escape_goto = new (GetAllocator()) HGoto();
3629 escape->AddInstruction(escape_instruction);
3630 escape->AddInstruction(escape_goto);
3631 escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment());
3632
3633 HInstruction* return_exit = new (GetAllocator()) HReturnVoid();
3634 breturn->AddInstruction(return_exit);
3635
3636 SetupExit(exit);
3637
3638 // PerformLSE expects this to be empty.
3639 graph_->ClearDominanceInformation();
3640 LOG(INFO) << "Pre LSE " << blks;
3641 PerformLSEWithPartial();
3642 LOG(INFO) << "Post LSE " << blks;
3643
3644 HNewInstance* moved_new_inst;
3645 HInstanceFieldSet* moved_set;
3646 std::tie(moved_new_inst, moved_set) =
3647 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_);
3648 HPhi* merge_phi = FindSingleInstruction<HPhi>(graph_, merge_crit_break);
3649 HPhi* alloc_phi = FindSingleInstruction<HPhi>(graph_, breturn);
3650 EXPECT_INS_EQ(moved_new_inst, moved_set->InputAt(0));
3651 ASSERT_NE(alloc_phi, nullptr);
3652 EXPECT_EQ(alloc_phi->InputAt(0), graph_->GetNullConstant())
3653 << alloc_phi->GetBlock()->GetPredecessors()[0]->GetBlockId() << " " << *alloc_phi;
3654 EXPECT_TRUE(alloc_phi->InputAt(1)->IsNewInstance()) << *alloc_phi;
3655 ASSERT_NE(merge_phi, nullptr);
3656 EXPECT_EQ(merge_phi->InputCount(), 2u);
3657 EXPECT_INS_EQ(merge_phi->InputAt(0), c10);
3658 EXPECT_INS_EQ(merge_phi->InputAt(1), c12);
3659 EXPECT_TRUE(merge_phi->GetUses().HasExactlyOneElement());
3660 EXPECT_INS_EQ(merge_phi->GetUses().front().GetUser(), moved_set);
3661 EXPECT_INS_RETAINED(escape_instruction);
3662 EXPECT_INS_EQ(escape_instruction->InputAt(0), moved_new_inst);
3663 // store removed or moved.
3664 EXPECT_NE(store_left->GetBlock(), left_set);
3665 EXPECT_NE(store_right->GetBlock(), left_set);
3666 // New-inst removed or moved.
3667 EXPECT_NE(new_inst->GetBlock(), entry);
3668 }
3669
3670 // // ENTRY
3671 // // To be moved
3672 // obj = new Obj();
3673 // switch(args) {
3674 // default:
3675 // return obj.a;
3676 // case b:
3677 // obj.a = 5; break;
3678 // case c:
3679 // obj.b = 4; break;
3680 // }
3681 // escape(obj);
3682 // return obj.a;
3683 // EXIT
TEST_F(LoadStoreEliminationTest,MovePredicatedAlloc3)3684 TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc3) {
3685 ScopedObjectAccess soa(Thread::Current());
3686 VariableSizedHandleScope vshs(soa.Self());
3687 CreateGraph(&vshs);
3688 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3689 "exit",
3690 {{"entry", "early_return"},
3691 {"entry", "set_one"},
3692 {"entry", "set_two"},
3693 {"early_return", "exit"},
3694 {"set_one", "escape"},
3695 {"set_two", "escape"},
3696 {"escape", "exit"}}));
3697 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3698 GET_BLOCK(entry);
3699 GET_BLOCK(exit);
3700 GET_BLOCK(escape);
3701 GET_BLOCK(early_return);
3702 GET_BLOCK(set_one);
3703 GET_BLOCK(set_two);
3704 #undef GET_BLOCK
3705 EnsurePredecessorOrder(escape, {set_one, set_two});
3706 HInstruction* int_val = MakeParam(DataType::Type::kInt32);
3707 HInstruction* c0 = graph_->GetIntConstant(0);
3708 HInstruction* c4 = graph_->GetIntConstant(4);
3709 HInstruction* c5 = graph_->GetIntConstant(5);
3710
3711 HInstruction* cls = MakeClassLoad();
3712 HInstruction* new_inst = MakeNewInstance(cls);
3713 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val);
3714 entry->AddInstruction(cls);
3715 entry->AddInstruction(new_inst);
3716 entry->AddInstruction(switch_inst);
3717 ManuallyBuildEnvFor(cls, {});
3718 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3719
3720 HInstruction* store_one = MakeIFieldSet(new_inst, c4, MemberOffset(32));
3721 HInstruction* goto_one = new (GetAllocator()) HGoto();
3722 set_one->AddInstruction(store_one);
3723 set_one->AddInstruction(goto_one);
3724
3725 HInstruction* store_two = MakeIFieldSet(new_inst, c5, MemberOffset(32));
3726 HInstruction* goto_two = new (GetAllocator()) HGoto();
3727 set_two->AddInstruction(store_two);
3728 set_two->AddInstruction(goto_two);
3729
3730 HInstruction* read_early = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3731 HInstruction* return_early = new (GetAllocator()) HReturn(read_early);
3732 early_return->AddInstruction(read_early);
3733 early_return->AddInstruction(return_early);
3734
3735 HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst });
3736 HInstruction* read_escape = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3737 HInstruction* return_escape = new (GetAllocator()) HReturn(read_escape);
3738 escape->AddInstruction(escape_instruction);
3739 escape->AddInstruction(read_escape);
3740 escape->AddInstruction(return_escape);
3741 escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment());
3742
3743 SetupExit(exit);
3744
3745 // PerformLSE expects this to be empty.
3746 graph_->ClearDominanceInformation();
3747 LOG(INFO) << "Pre LSE " << blks;
3748 PerformLSEWithPartial();
3749 LOG(INFO) << "Post LSE " << blks;
3750
3751 // Each escaping switch path gets its own materialization block.
3752 // Blocks:
3753 // early_return(5) -> [exit(4)]
3754 // entry(3) -> [early_return(5), <Unnamed>(9), <Unnamed>(10)]
3755 // escape(8) -> [exit(4)]
3756 // exit(4) -> []
3757 // set_one(6) -> [escape(8)]
3758 // set_two(7) -> [escape(8)]
3759 // <Unnamed>(10) -> [set_two(7)]
3760 // <Unnamed>(9) -> [set_one(6)]
3761 HBasicBlock* materialize_one = set_one->GetSinglePredecessor();
3762 HBasicBlock* materialize_two = set_two->GetSinglePredecessor();
3763 HNewInstance* materialization_ins_one =
3764 FindSingleInstruction<HNewInstance>(graph_, materialize_one);
3765 HNewInstance* materialization_ins_two =
3766 FindSingleInstruction<HNewInstance>(graph_, materialize_two);
3767 HPhi* new_phi = FindSingleInstruction<HPhi>(graph_, escape);
3768 EXPECT_NE(materialization_ins_one, nullptr);
3769 EXPECT_NE(materialization_ins_two, nullptr);
3770 EXPECT_EQ(materialization_ins_one, new_phi->InputAt(0))
3771 << *materialization_ins_one << " vs " << *new_phi;
3772 EXPECT_EQ(materialization_ins_two, new_phi->InputAt(1))
3773 << *materialization_ins_two << " vs " << *new_phi;
3774
3775 EXPECT_INS_RETAINED(escape_instruction);
3776 EXPECT_INS_RETAINED(read_escape);
3777 EXPECT_EQ(read_escape->InputAt(0), new_phi) << *new_phi << " vs " << *read_escape->InputAt(0);
3778 EXPECT_EQ(store_one->InputAt(0), materialization_ins_one);
3779 EXPECT_EQ(store_two->InputAt(0), materialization_ins_two);
3780 EXPECT_EQ(escape_instruction->InputAt(0), new_phi);
3781 EXPECT_INS_REMOVED(read_early);
3782 EXPECT_EQ(return_early->InputAt(0), c0);
3783 }
3784
3785 // // ENTRY
3786 // // To be moved
3787 // obj = new Obj();
3788 // switch(args) {
3789 // case a:
3790 // // set_one_and_escape
3791 // obj.a = 5;
3792 // escape(obj);
3793 // // FALLTHROUGH
3794 // case c:
3795 // // set_two
3796 // obj.a = 4; break;
3797 // default:
3798 // return obj.a;
3799 // }
3800 // escape(obj);
3801 // return obj.a;
3802 // EXIT
TEST_F(LoadStoreEliminationTest,MovePredicatedAlloc4)3803 TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc4) {
3804 ScopedObjectAccess soa(Thread::Current());
3805 VariableSizedHandleScope vshs(soa.Self());
3806 CreateGraph(&vshs);
3807 // Break the critical edge between entry and set_two with the
3808 // set_two_critical_break node. Graph simplification would do this for us if
3809 // we didn't do it manually. This way we have a nice-name for debugging and
3810 // testing.
3811 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3812 "exit",
3813 {{"entry", "early_return"},
3814 {"entry", "set_one_and_escape"},
3815 {"entry", "set_two_critical_break"},
3816 {"set_two_critical_break", "set_two"},
3817 {"early_return", "exit"},
3818 {"set_one_and_escape", "set_two"},
3819 {"set_two", "escape"},
3820 {"escape", "exit"}}));
3821 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3822 GET_BLOCK(entry);
3823 GET_BLOCK(exit);
3824 GET_BLOCK(escape);
3825 GET_BLOCK(early_return);
3826 GET_BLOCK(set_one_and_escape);
3827 GET_BLOCK(set_two);
3828 GET_BLOCK(set_two_critical_break);
3829 #undef GET_BLOCK
3830 EnsurePredecessorOrder(set_two, {set_one_and_escape, set_two_critical_break});
3831 HInstruction* int_val = MakeParam(DataType::Type::kInt32);
3832 HInstruction* c0 = graph_->GetIntConstant(0);
3833 HInstruction* c4 = graph_->GetIntConstant(4);
3834 HInstruction* c5 = graph_->GetIntConstant(5);
3835
3836 HInstruction* cls = MakeClassLoad();
3837 HInstruction* new_inst = MakeNewInstance(cls);
3838 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val);
3839 entry->AddInstruction(cls);
3840 entry->AddInstruction(new_inst);
3841 entry->AddInstruction(switch_inst);
3842 ManuallyBuildEnvFor(cls, {});
3843 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3844
3845 HInstruction* store_one = MakeIFieldSet(new_inst, c4, MemberOffset(32));
3846 HInstruction* escape_one = MakeInvoke(DataType::Type::kVoid, { new_inst });
3847 HInstruction* goto_one = new (GetAllocator()) HGoto();
3848 set_one_and_escape->AddInstruction(store_one);
3849 set_one_and_escape->AddInstruction(escape_one);
3850 set_one_and_escape->AddInstruction(goto_one);
3851 escape_one->CopyEnvironmentFrom(cls->GetEnvironment());
3852
3853 HInstruction* goto_crit_break = new (GetAllocator()) HGoto();
3854 set_two_critical_break->AddInstruction(goto_crit_break);
3855
3856 HInstruction* store_two = MakeIFieldSet(new_inst, c5, MemberOffset(32));
3857 HInstruction* goto_two = new (GetAllocator()) HGoto();
3858 set_two->AddInstruction(store_two);
3859 set_two->AddInstruction(goto_two);
3860
3861 HInstruction* read_early = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3862 HInstruction* return_early = new (GetAllocator()) HReturn(read_early);
3863 early_return->AddInstruction(read_early);
3864 early_return->AddInstruction(return_early);
3865
3866 HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst });
3867 HInstruction* read_escape = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3868 HInstruction* return_escape = new (GetAllocator()) HReturn(read_escape);
3869 escape->AddInstruction(escape_instruction);
3870 escape->AddInstruction(read_escape);
3871 escape->AddInstruction(return_escape);
3872 escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment());
3873
3874 SetupExit(exit);
3875
3876 // PerformLSE expects this to be empty.
3877 graph_->ClearDominanceInformation();
3878 LOG(INFO) << "Pre LSE " << blks;
3879 PerformLSEWithPartial();
3880 LOG(INFO) << "Post LSE " << blks;
3881
3882 EXPECT_INS_REMOVED(read_early);
3883 EXPECT_EQ(return_early->InputAt(0), c0);
3884 // Each escaping switch path gets its own materialization block.
3885 // Blocks:
3886 // early_return(5) -> [exit(4)]
3887 // entry(3) -> [early_return(5), <Unnamed>(10), <Unnamed>(11)]
3888 // escape(9) -> [exit(4)]
3889 // exit(4) -> []
3890 // set_one_and_escape(6) -> [set_two(8)]
3891 // set_two(8) -> [escape(9)]
3892 // set_two_critical_break(7) -> [set_two(8)]
3893 // <Unnamed>(11) -> [set_two_critical_break(7)]
3894 // <Unnamed>(10) -> [set_one_and_escape(6)]
3895 HBasicBlock* materialize_one = set_one_and_escape->GetSinglePredecessor();
3896 HBasicBlock* materialize_two = set_two_critical_break->GetSinglePredecessor();
3897 HNewInstance* materialization_ins_one =
3898 FindSingleInstruction<HNewInstance>(graph_, materialize_one);
3899 HNewInstance* materialization_ins_two =
3900 FindSingleInstruction<HNewInstance>(graph_, materialize_two);
3901 HPhi* new_phi = FindSingleInstruction<HPhi>(graph_, set_two);
3902 ASSERT_NE(new_phi, nullptr);
3903 ASSERT_NE(materialization_ins_one, nullptr);
3904 ASSERT_NE(materialization_ins_two, nullptr);
3905 EXPECT_INS_EQ(materialization_ins_one, new_phi->InputAt(0));
3906 EXPECT_INS_EQ(materialization_ins_two, new_phi->InputAt(1));
3907
3908 EXPECT_INS_EQ(store_one->InputAt(0), materialization_ins_one);
3909 EXPECT_INS_EQ(store_two->InputAt(0), new_phi) << *store_two << " vs " << *new_phi;
3910 EXPECT_INS_EQ(escape_instruction->InputAt(0), new_phi);
3911 EXPECT_INS_RETAINED(escape_one);
3912 EXPECT_INS_EQ(escape_one->InputAt(0), materialization_ins_one);
3913 EXPECT_INS_RETAINED(escape_instruction);
3914 EXPECT_INS_RETAINED(read_escape);
3915 EXPECT_EQ(read_escape->InputAt(0), new_phi) << *new_phi << " vs " << *read_escape->InputAt(0);
3916 }
3917
3918 // // ENTRY
3919 // // To be moved
3920 // obj = new Obj();
3921 // switch(args) {
3922 // case a:
3923 // // set_one
3924 // obj.a = 5;
3925 // // nb passthrough
3926 // case c:
3927 // // set_two_and_escape
3928 // obj.a += 4;
3929 // escape(obj);
3930 // break;
3931 // default:
3932 // obj.a = 10;
3933 // }
3934 // return obj.a;
3935 // EXIT
TEST_F(LoadStoreEliminationTest,MovePredicatedAlloc5)3936 TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc5) {
3937 ScopedObjectAccess soa(Thread::Current());
3938 VariableSizedHandleScope vshs(soa.Self());
3939 CreateGraph(&vshs);
3940 // Break the critical edge between entry and set_two with the
3941 // set_two_critical_break node. Graph simplification would do this for us if
3942 // we didn't do it manually. This way we have a nice-name for debugging and
3943 // testing.
3944 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3945 "exit",
3946 {{"entry", "set_noescape"},
3947 {"entry", "set_one"},
3948 {"entry", "set_two_critical_break"},
3949 {"set_two_critical_break", "set_two_and_escape"},
3950 {"set_noescape", "breturn"},
3951 {"set_one", "set_two_and_escape"},
3952 {"set_two_and_escape", "breturn"},
3953 {"breturn", "exit"}}));
3954 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3955 GET_BLOCK(entry);
3956 GET_BLOCK(exit);
3957 GET_BLOCK(breturn);
3958 GET_BLOCK(set_noescape);
3959 GET_BLOCK(set_one);
3960 GET_BLOCK(set_two_and_escape);
3961 GET_BLOCK(set_two_critical_break);
3962 #undef GET_BLOCK
3963 EnsurePredecessorOrder(set_two_and_escape, {set_one, set_two_critical_break});
3964 EnsurePredecessorOrder(breturn, {set_two_and_escape, set_noescape});
3965 HInstruction* int_val = MakeParam(DataType::Type::kInt32);
3966 HInstruction* c0 = graph_->GetIntConstant(0);
3967 HInstruction* c4 = graph_->GetIntConstant(4);
3968 HInstruction* c5 = graph_->GetIntConstant(5);
3969 HInstruction* c10 = graph_->GetIntConstant(10);
3970
3971 HInstruction* cls = MakeClassLoad();
3972 HInstruction* new_inst = MakeNewInstance(cls);
3973 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val);
3974 entry->AddInstruction(cls);
3975 entry->AddInstruction(new_inst);
3976 entry->AddInstruction(switch_inst);
3977 ManuallyBuildEnvFor(cls, {});
3978 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3979
3980 HInstruction* store_one = MakeIFieldSet(new_inst, c5, MemberOffset(32));
3981 HInstruction* goto_one = new (GetAllocator()) HGoto();
3982 set_one->AddInstruction(store_one);
3983 set_one->AddInstruction(goto_one);
3984
3985 HInstruction* goto_crit_break = new (GetAllocator()) HGoto();
3986 set_two_critical_break->AddInstruction(goto_crit_break);
3987
3988 HInstruction* get_two = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3989 HInstruction* add_two = new (GetAllocator()) HAdd(DataType::Type::kInt32, get_two, c4);
3990 HInstruction* store_two = MakeIFieldSet(new_inst, add_two, MemberOffset(32));
3991 HInstruction* escape_two = MakeInvoke(DataType::Type::kVoid, {new_inst});
3992 HInstruction* goto_two = new (GetAllocator()) HGoto();
3993 set_two_and_escape->AddInstruction(get_two);
3994 set_two_and_escape->AddInstruction(add_two);
3995 set_two_and_escape->AddInstruction(store_two);
3996 set_two_and_escape->AddInstruction(escape_two);
3997 set_two_and_escape->AddInstruction(goto_two);
3998 escape_two->CopyEnvironmentFrom(cls->GetEnvironment());
3999
4000 HInstruction* store_noescape = MakeIFieldSet(new_inst, c10, MemberOffset(32));
4001 HInstruction* goto_noescape = new (GetAllocator()) HGoto();
4002 set_noescape->AddInstruction(store_noescape);
4003 set_noescape->AddInstruction(goto_noescape);
4004
4005 HInstruction* read_breturn = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4006 HInstruction* return_breturn = new (GetAllocator()) HReturn(read_breturn);
4007 breturn->AddInstruction(read_breturn);
4008 breturn->AddInstruction(return_breturn);
4009
4010 SetupExit(exit);
4011
4012 // PerformLSE expects this to be empty.
4013 graph_->ClearDominanceInformation();
4014 LOG(INFO) << "Pre LSE " << blks;
4015 PerformLSEWithPartial();
4016 LOG(INFO) << "Post LSE " << blks;
4017
4018 // Normal LSE can get rid of these two.
4019 EXPECT_INS_REMOVED(store_one);
4020 EXPECT_INS_REMOVED(get_two);
4021 EXPECT_INS_RETAINED(add_two);
4022 EXPECT_TRUE(add_two->InputAt(0)->IsPhi());
4023 EXPECT_INS_EQ(add_two->InputAt(0)->InputAt(0), c5);
4024 EXPECT_INS_EQ(add_two->InputAt(0)->InputAt(1), c0);
4025 EXPECT_INS_EQ(add_two->InputAt(1), c4);
4026
4027 HBasicBlock* materialize_one = set_one->GetSinglePredecessor();
4028 HBasicBlock* materialize_two = set_two_critical_break->GetSinglePredecessor();
4029 HNewInstance* materialization_ins_one =
4030 FindSingleInstruction<HNewInstance>(graph_, materialize_one);
4031 HNewInstance* materialization_ins_two =
4032 FindSingleInstruction<HNewInstance>(graph_, materialize_two);
4033 std::vector<HPhi*> phis;
4034 std::tie(phis) = FindAllInstructions<HPhi>(graph_, set_two_and_escape);
4035 HPhi* new_phi = FindOrNull(
4036 phis.begin(), phis.end(), [&](auto p) { return p->GetType() == DataType::Type::kReference; });
4037 ASSERT_NE(new_phi, nullptr);
4038 ASSERT_NE(materialization_ins_one, nullptr);
4039 ASSERT_NE(materialization_ins_two, nullptr);
4040 EXPECT_INS_EQ(materialization_ins_one, new_phi->InputAt(0));
4041 EXPECT_INS_EQ(materialization_ins_two, new_phi->InputAt(1));
4042
4043 HPredicatedInstanceFieldGet* pred_get =
4044 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
4045 EXPECT_TRUE(pred_get->GetTarget()->IsPhi());
4046 EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(0), new_phi);
4047 EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(1), graph_->GetNullConstant());
4048
4049 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), c0);
4050 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), c10);
4051 }
4052
4053 // // ENTRY
4054 // obj = new Obj();
4055 // if (parameter_value) {
4056 // // LEFT
4057 // // DO NOT ELIMINATE
4058 // obj.field = 1;
4059 // escape(obj);
4060 // return obj.field;
4061 // } else {
4062 // // RIGHT
4063 // // ELIMINATE
4064 // obj.field = 2;
4065 // return obj.field;
4066 // }
4067 // EXIT
TEST_F(LoadStoreEliminationTest,PartialLoadElimination3)4068 TEST_F(LoadStoreEliminationTest, PartialLoadElimination3) {
4069 ScopedObjectAccess soa(Thread::Current());
4070 VariableSizedHandleScope vshs(soa.Self());
4071 CreateGraph(&vshs);
4072 AdjacencyListGraph blks(SetupFromAdjacencyList(
4073 "entry",
4074 "exit",
4075 {{"entry", "left"}, {"entry", "right"}, {"left", "exit"}, {"right", "exit"}}));
4076 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4077 GET_BLOCK(entry);
4078 GET_BLOCK(exit);
4079 GET_BLOCK(left);
4080 GET_BLOCK(right);
4081 #undef GET_BLOCK
4082 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4083 HInstruction* c1 = graph_->GetIntConstant(1);
4084 HInstruction* c2 = graph_->GetIntConstant(2);
4085
4086 HInstruction* cls = MakeClassLoad();
4087 HInstruction* new_inst = MakeNewInstance(cls);
4088 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4089 entry->AddInstruction(cls);
4090 entry->AddInstruction(new_inst);
4091 entry->AddInstruction(if_inst);
4092 ManuallyBuildEnvFor(cls, {});
4093 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4094
4095 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
4096 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4097 HInstruction* read_left = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4098 HInstruction* return_left = new (GetAllocator()) HReturn(read_left);
4099 left->AddInstruction(write_left);
4100 left->AddInstruction(call_left);
4101 left->AddInstruction(read_left);
4102 left->AddInstruction(return_left);
4103 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4104
4105 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4106 HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4107 HInstruction* return_right = new (GetAllocator()) HReturn(read_right);
4108 right->AddInstruction(write_right);
4109 right->AddInstruction(read_right);
4110 right->AddInstruction(return_right);
4111
4112 SetupExit(exit);
4113
4114 // PerformLSE expects this to be empty.
4115 graph_->ClearDominanceInformation();
4116 PerformLSE();
4117
4118 EXPECT_INS_REMOVED(read_right);
4119 EXPECT_INS_REMOVED(write_right);
4120 EXPECT_INS_RETAINED(write_left);
4121 EXPECT_INS_RETAINED(call_left);
4122 EXPECT_INS_RETAINED(read_left);
4123 }
4124
4125 // // ENTRY
4126 // obj = new Obj();
4127 // if (parameter_value) {
4128 // // LEFT
4129 // // DO NOT ELIMINATE
4130 // obj.field = 1;
4131 // while (true) {
4132 // bool esc = escape(obj);
4133 // // DO NOT ELIMINATE
4134 // obj.field = 3;
4135 // if (esc) break;
4136 // }
4137 // // ELIMINATE.
4138 // return obj.field;
4139 // } else {
4140 // // RIGHT
4141 // // ELIMINATE
4142 // obj.field = 2;
4143 // return obj.field;
4144 // }
4145 // EXIT
TEST_F(LoadStoreEliminationTest,PartialLoadElimination4)4146 TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) {
4147 ScopedObjectAccess soa(Thread::Current());
4148 VariableSizedHandleScope vshs(soa.Self());
4149 CreateGraph(&vshs);
4150 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4151 "exit",
4152 {{"entry", "entry_post"},
4153 {"entry_post", "right"},
4154 {"right", "exit"},
4155 {"entry_post", "left_pre"},
4156 {"left_pre", "left_loop"},
4157 {"left_loop", "left_loop"},
4158 {"left_loop", "left_finish"},
4159 {"left_finish", "exit"}}));
4160 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4161 GET_BLOCK(entry);
4162 GET_BLOCK(entry_post);
4163 GET_BLOCK(exit);
4164 GET_BLOCK(left_pre);
4165 GET_BLOCK(left_loop);
4166 GET_BLOCK(left_finish);
4167 GET_BLOCK(right);
4168 #undef GET_BLOCK
4169 // Left-loops first successor is the break.
4170 if (left_loop->GetSuccessors()[0] != left_finish) {
4171 left_loop->SwapSuccessors();
4172 }
4173 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4174 HInstruction* c1 = graph_->GetIntConstant(1);
4175 HInstruction* c2 = graph_->GetIntConstant(2);
4176 HInstruction* c3 = graph_->GetIntConstant(3);
4177
4178 HInstruction* cls = MakeClassLoad();
4179 HInstruction* new_inst = MakeNewInstance(cls);
4180 HInstruction* goto_entry = new (GetAllocator()) HGoto();
4181 entry->AddInstruction(cls);
4182 entry->AddInstruction(new_inst);
4183 entry->AddInstruction(goto_entry);
4184 ManuallyBuildEnvFor(cls, {});
4185 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4186
4187 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4188 entry_post->AddInstruction(if_inst);
4189
4190 HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32));
4191 HInstruction* goto_left_pre = new (GetAllocator()) HGoto();
4192 left_pre->AddInstruction(write_left_pre);
4193 left_pre->AddInstruction(goto_left_pre);
4194
4195 HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck();
4196 HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, { new_inst });
4197 HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32));
4198 HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop);
4199 left_loop->AddInstruction(suspend_left_loop);
4200 left_loop->AddInstruction(call_left_loop);
4201 left_loop->AddInstruction(write_left_loop);
4202 left_loop->AddInstruction(if_left_loop);
4203 suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4204 call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4205
4206 HInstruction* read_left_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4207 HInstruction* return_left_end = new (GetAllocator()) HReturn(read_left_end);
4208 left_finish->AddInstruction(read_left_end);
4209 left_finish->AddInstruction(return_left_end);
4210
4211 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4212 HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4213 HInstruction* return_right = new (GetAllocator()) HReturn(read_right);
4214 right->AddInstruction(write_right);
4215 right->AddInstruction(read_right);
4216 right->AddInstruction(return_right);
4217
4218 SetupExit(exit);
4219
4220 // PerformLSE expects this to be empty.
4221 graph_->ClearDominanceInformation();
4222 PerformLSE();
4223
4224 EXPECT_INS_RETAINED(write_left_pre);
4225 EXPECT_INS_REMOVED(read_right);
4226 EXPECT_INS_REMOVED(write_right);
4227 EXPECT_INS_RETAINED(write_left_loop);
4228 EXPECT_INS_RETAINED(call_left_loop);
4229 EXPECT_INS_REMOVED(read_left_end);
4230 }
4231
4232 // // ENTRY
4233 // obj = new Obj();
4234 // if (parameter_value) {
4235 // // LEFT
4236 // // DO NOT ELIMINATE
4237 // escape(obj);
4238 // obj.field = 1;
4239 // } else {
4240 // // RIGHT
4241 // // obj hasn't escaped so it's invisible.
4242 // // ELIMINATE
4243 // obj.field = 2;
4244 // noescape();
4245 // }
4246 // EXIT
4247 // ELIMINATE
4248 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialLoadElimination5)4249 TEST_F(LoadStoreEliminationTest, PartialLoadElimination5) {
4250 ScopedObjectAccess soa(Thread::Current());
4251 VariableSizedHandleScope vshs(soa.Self());
4252 CreateGraph(&vshs);
4253 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4254 "exit",
4255 {{"entry", "left"},
4256 {"entry", "right"},
4257 {"left", "breturn"},
4258 {"right", "breturn"},
4259 {"breturn", "exit"}}));
4260 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4261 GET_BLOCK(entry);
4262 GET_BLOCK(exit);
4263 GET_BLOCK(breturn);
4264 GET_BLOCK(left);
4265 GET_BLOCK(right);
4266 #undef GET_BLOCK
4267 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4268 HInstruction* c1 = graph_->GetIntConstant(1);
4269 HInstruction* c2 = graph_->GetIntConstant(2);
4270
4271 HInstruction* cls = MakeClassLoad();
4272 HInstruction* new_inst = MakeNewInstance(cls);
4273 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4274 entry->AddInstruction(cls);
4275 entry->AddInstruction(new_inst);
4276 entry->AddInstruction(if_inst);
4277 ManuallyBuildEnvFor(cls, {});
4278 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4279
4280 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4281 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
4282 HInstruction* goto_left = new (GetAllocator()) HGoto();
4283 left->AddInstruction(call_left);
4284 left->AddInstruction(write_left);
4285 left->AddInstruction(goto_left);
4286 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4287
4288 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4289 HInstruction* call_right = MakeInvoke(DataType::Type::kVoid, {});
4290 HInstruction* goto_right = new (GetAllocator()) HGoto();
4291 right->AddInstruction(write_right);
4292 right->AddInstruction(call_right);
4293 right->AddInstruction(goto_right);
4294 call_right->CopyEnvironmentFrom(cls->GetEnvironment());
4295
4296 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4297 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
4298 breturn->AddInstruction(read_bottom);
4299 breturn->AddInstruction(return_exit);
4300
4301 SetupExit(exit);
4302
4303 // PerformLSE expects this to be empty.
4304 graph_->ClearDominanceInformation();
4305 PerformLSE();
4306
4307 EXPECT_INS_REMOVED(read_bottom);
4308 EXPECT_INS_REMOVED(write_right);
4309 EXPECT_INS_RETAINED(write_left);
4310 EXPECT_INS_RETAINED(call_left);
4311 EXPECT_INS_RETAINED(call_right);
4312 }
4313
4314 // // ENTRY
4315 // obj = new Obj();
4316 // // Eliminate this one. Object hasn't escaped yet so it's safe.
4317 // obj.field = 3;
4318 // noescape();
4319 // if (parameter_value) {
4320 // // LEFT
4321 // // DO NOT ELIMINATE
4322 // obj.field = 5;
4323 // escape(obj);
4324 // obj.field = 1;
4325 // } else {
4326 // // RIGHT
4327 // // ELIMINATE
4328 // obj.field = 2;
4329 // }
4330 // EXIT
4331 // ELIMINATE
4332 // return obj.fid
TEST_F(LoadStoreEliminationTest,PartialLoadElimination6)4333 TEST_F(LoadStoreEliminationTest, PartialLoadElimination6) {
4334 ScopedObjectAccess soa(Thread::Current());
4335 VariableSizedHandleScope vshs(soa.Self());
4336 CreateGraph(&vshs);
4337 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4338 "exit",
4339 {{"entry", "left"},
4340 {"entry", "right"},
4341 {"left", "breturn"},
4342 {"right", "breturn"},
4343 {"breturn", "exit"}}));
4344 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4345 GET_BLOCK(entry);
4346 GET_BLOCK(exit);
4347 GET_BLOCK(breturn);
4348 GET_BLOCK(left);
4349 GET_BLOCK(right);
4350 #undef GET_BLOCK
4351 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4352 HInstruction* c1 = graph_->GetIntConstant(1);
4353 HInstruction* c2 = graph_->GetIntConstant(2);
4354 HInstruction* c3 = graph_->GetIntConstant(3);
4355 HInstruction* c5 = graph_->GetIntConstant(5);
4356
4357 HInstruction* cls = MakeClassLoad();
4358 HInstruction* new_inst = MakeNewInstance(cls);
4359 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
4360 HInstruction* call_entry = MakeInvoke(DataType::Type::kVoid, {});
4361 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4362 entry->AddInstruction(cls);
4363 entry->AddInstruction(new_inst);
4364 entry->AddInstruction(write_entry);
4365 entry->AddInstruction(call_entry);
4366 entry->AddInstruction(if_inst);
4367 ManuallyBuildEnvFor(cls, {});
4368 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4369 call_entry->CopyEnvironmentFrom(cls->GetEnvironment());
4370
4371 HInstruction* write_left_start = MakeIFieldSet(new_inst, c5, MemberOffset(32));
4372 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4373 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
4374 HInstruction* goto_left = new (GetAllocator()) HGoto();
4375 left->AddInstruction(write_left_start);
4376 left->AddInstruction(call_left);
4377 left->AddInstruction(write_left);
4378 left->AddInstruction(goto_left);
4379 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4380
4381 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4382 HInstruction* goto_right = new (GetAllocator()) HGoto();
4383 right->AddInstruction(write_right);
4384 right->AddInstruction(goto_right);
4385
4386 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4387 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
4388 breturn->AddInstruction(read_bottom);
4389 breturn->AddInstruction(return_exit);
4390
4391 SetupExit(exit);
4392
4393 // PerformLSE expects this to be empty.
4394 graph_->ClearDominanceInformation();
4395 PerformLSE();
4396
4397 EXPECT_INS_REMOVED(read_bottom);
4398 EXPECT_INS_REMOVED(write_right);
4399 EXPECT_INS_REMOVED(write_entry);
4400 EXPECT_INS_RETAINED(write_left_start);
4401 EXPECT_INS_RETAINED(write_left);
4402 EXPECT_INS_RETAINED(call_left);
4403 EXPECT_INS_RETAINED(call_entry);
4404 }
4405
4406 // // ENTRY
4407 // obj = new Obj();
4408 // if (parameter_value) {
4409 // // LEFT
4410 // // DO NOT ELIMINATE
4411 // obj.field = 1;
4412 // while (true) {
4413 // bool esc = escape(obj);
4414 // if (esc) break;
4415 // // DO NOT ELIMINATE
4416 // obj.field = 3;
4417 // }
4418 // } else {
4419 // // RIGHT
4420 // // DO NOT ELIMINATE
4421 // obj.field = 2;
4422 // }
4423 // // DO NOT ELIMINATE
4424 // return obj.field;
4425 // EXIT
TEST_F(LoadStoreEliminationTest,PartialLoadPreserved3)4426 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved3) {
4427 ScopedObjectAccess soa(Thread::Current());
4428 VariableSizedHandleScope vshs(soa.Self());
4429 CreateGraph(&vshs);
4430 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4431 "exit",
4432 {{"entry", "entry_post"},
4433 {"entry_post", "right"},
4434 {"right", "return_block"},
4435 {"entry_post", "left_pre"},
4436 {"left_pre", "left_loop"},
4437 {"left_loop", "left_loop_post"},
4438 {"left_loop_post", "left_loop"},
4439 {"left_loop", "return_block"},
4440 {"return_block", "exit"}}));
4441 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4442 GET_BLOCK(entry);
4443 GET_BLOCK(entry_post);
4444 GET_BLOCK(exit);
4445 GET_BLOCK(return_block);
4446 GET_BLOCK(left_pre);
4447 GET_BLOCK(left_loop);
4448 GET_BLOCK(left_loop_post);
4449 GET_BLOCK(right);
4450 #undef GET_BLOCK
4451 // Left-loops first successor is the break.
4452 if (left_loop->GetSuccessors()[0] != return_block) {
4453 left_loop->SwapSuccessors();
4454 }
4455 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4456 HInstruction* c1 = graph_->GetIntConstant(1);
4457 HInstruction* c2 = graph_->GetIntConstant(2);
4458 HInstruction* c3 = graph_->GetIntConstant(3);
4459
4460 HInstruction* cls = MakeClassLoad();
4461 HInstruction* new_inst = MakeNewInstance(cls);
4462 HInstruction* goto_entry = new (GetAllocator()) HGoto();
4463 entry->AddInstruction(cls);
4464 entry->AddInstruction(new_inst);
4465 entry->AddInstruction(goto_entry);
4466 ManuallyBuildEnvFor(cls, {});
4467 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4468
4469 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4470 entry_post->AddInstruction(if_inst);
4471
4472 HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32));
4473 HInstruction* goto_left_pre = new (GetAllocator()) HGoto();
4474 left_pre->AddInstruction(write_left_pre);
4475 left_pre->AddInstruction(goto_left_pre);
4476
4477 HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck();
4478 HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, { new_inst });
4479 HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop);
4480 left_loop->AddInstruction(suspend_left_loop);
4481 left_loop->AddInstruction(call_left_loop);
4482 left_loop->AddInstruction(if_left_loop);
4483 suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4484 call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4485
4486 HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32));
4487 HInstruction* goto_left_loop = new (GetAllocator()) HGoto();
4488 left_loop_post->AddInstruction(write_left_loop);
4489 left_loop_post->AddInstruction(goto_left_loop);
4490
4491 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4492 HInstruction* goto_right = new (GetAllocator()) HGoto();
4493 right->AddInstruction(write_right);
4494 right->AddInstruction(goto_right);
4495
4496 HInstruction* read_return = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4497 HInstruction* return_final = new (GetAllocator()) HReturn(read_return);
4498 return_block->AddInstruction(read_return);
4499 return_block->AddInstruction(return_final);
4500
4501 SetupExit(exit);
4502
4503 // PerformLSE expects this to be empty.
4504 graph_->ClearDominanceInformation();
4505 PerformLSENoPartial();
4506
4507 EXPECT_INS_RETAINED(write_left_pre) << *write_left_pre;
4508 EXPECT_INS_RETAINED(read_return) << *read_return;
4509 EXPECT_INS_RETAINED(write_right) << *write_right;
4510 EXPECT_INS_RETAINED(write_left_loop) << *write_left_loop;
4511 EXPECT_INS_RETAINED(call_left_loop) << *call_left_loop;
4512 }
4513
4514 // // ENTRY
4515 // obj = new Obj();
4516 // if (parameter_value) {
4517 // // LEFT
4518 // // ELIMINATE (not visible since always overridden by obj.field = 3)
4519 // obj.field = 1;
4520 // while (true) {
4521 // bool stop = should_stop();
4522 // // DO NOT ELIMINATE (visible by read at end)
4523 // obj.field = 3;
4524 // if (stop) break;
4525 // }
4526 // } else {
4527 // // RIGHT
4528 // // DO NOT ELIMINATE
4529 // obj.field = 2;
4530 // escape(obj);
4531 // }
4532 // // DO NOT ELIMINATE
4533 // return obj.field;
4534 // EXIT
TEST_F(LoadStoreEliminationTest,PartialLoadPreserved4)4535 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) {
4536 ScopedObjectAccess soa(Thread::Current());
4537 VariableSizedHandleScope vshs(soa.Self());
4538 CreateGraph(&vshs);
4539 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4540 "exit",
4541 {{"entry", "entry_post"},
4542 {"entry_post", "right"},
4543 {"right", "return_block"},
4544 {"entry_post", "left_pre"},
4545 {"left_pre", "left_loop"},
4546 {"left_loop", "left_loop"},
4547 {"left_loop", "return_block"},
4548 {"return_block", "exit"}}));
4549 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4550 GET_BLOCK(entry);
4551 GET_BLOCK(entry_post);
4552 GET_BLOCK(exit);
4553 GET_BLOCK(return_block);
4554 GET_BLOCK(left_pre);
4555 GET_BLOCK(left_loop);
4556 GET_BLOCK(right);
4557 #undef GET_BLOCK
4558 // Left-loops first successor is the break.
4559 if (left_loop->GetSuccessors()[0] != return_block) {
4560 left_loop->SwapSuccessors();
4561 }
4562 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4563 HInstruction* c1 = graph_->GetIntConstant(1);
4564 HInstruction* c2 = graph_->GetIntConstant(2);
4565 HInstruction* c3 = graph_->GetIntConstant(3);
4566
4567 HInstruction* cls = MakeClassLoad();
4568 HInstruction* new_inst = MakeNewInstance(cls);
4569 HInstruction* goto_entry = new (GetAllocator()) HGoto();
4570 entry->AddInstruction(cls);
4571 entry->AddInstruction(new_inst);
4572 entry->AddInstruction(goto_entry);
4573 ManuallyBuildEnvFor(cls, {});
4574 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4575
4576 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4577 entry_post->AddInstruction(if_inst);
4578
4579 HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32));
4580 HInstruction* goto_left_pre = new (GetAllocator()) HGoto();
4581 left_pre->AddInstruction(write_left_pre);
4582 left_pre->AddInstruction(goto_left_pre);
4583
4584 HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck();
4585 HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, {});
4586 HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32));
4587 HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop);
4588 left_loop->AddInstruction(suspend_left_loop);
4589 left_loop->AddInstruction(call_left_loop);
4590 left_loop->AddInstruction(write_left_loop);
4591 left_loop->AddInstruction(if_left_loop);
4592 suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4593 call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4594
4595 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4596 HInstruction* call_right = MakeInvoke(DataType::Type::kBool, { new_inst });
4597 HInstruction* goto_right = new (GetAllocator()) HGoto();
4598 right->AddInstruction(write_right);
4599 right->AddInstruction(call_right);
4600 right->AddInstruction(goto_right);
4601 call_right->CopyEnvironmentFrom(cls->GetEnvironment());
4602
4603 HInstruction* read_return = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4604 HInstruction* return_final = new (GetAllocator()) HReturn(read_return);
4605 return_block->AddInstruction(read_return);
4606 return_block->AddInstruction(return_final);
4607
4608 SetupExit(exit);
4609
4610 // PerformLSE expects this to be empty.
4611 graph_->ClearDominanceInformation();
4612 PerformLSENoPartial();
4613
4614 EXPECT_INS_RETAINED(read_return);
4615 EXPECT_INS_RETAINED(write_right);
4616 EXPECT_INS_RETAINED(write_left_loop);
4617 EXPECT_INS_RETAINED(call_left_loop);
4618 EXPECT_INS_REMOVED(write_left_pre);
4619 EXPECT_INS_RETAINED(call_right);
4620 }
4621
4622 // // ENTRY
4623 // obj = new Obj();
4624 // if (parameter_value) {
4625 // // LEFT
4626 // // DO NOT ELIMINATE
4627 // escape(obj);
4628 // obj.field = 1;
4629 // // obj has already escaped so can't use field = 1 for value
4630 // noescape();
4631 // } else {
4632 // // RIGHT
4633 // // obj is needed for read since we don't know what the left value is
4634 // // DO NOT ELIMINATE
4635 // obj.field = 2;
4636 // noescape();
4637 // }
4638 // EXIT
4639 // ELIMINATE
4640 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialLoadPreserved5)4641 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) {
4642 ScopedObjectAccess soa(Thread::Current());
4643 VariableSizedHandleScope vshs(soa.Self());
4644 CreateGraph(&vshs);
4645 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4646 "exit",
4647 {{"entry", "left"},
4648 {"entry", "right"},
4649 {"left", "breturn"},
4650 {"right", "breturn"},
4651 {"breturn", "exit"}}));
4652 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4653 GET_BLOCK(entry);
4654 GET_BLOCK(exit);
4655 GET_BLOCK(breturn);
4656 GET_BLOCK(left);
4657 GET_BLOCK(right);
4658 #undef GET_BLOCK
4659 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4660 HInstruction* c1 = graph_->GetIntConstant(1);
4661 HInstruction* c2 = graph_->GetIntConstant(2);
4662
4663 HInstruction* cls = MakeClassLoad();
4664 HInstruction* new_inst = MakeNewInstance(cls);
4665 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4666 entry->AddInstruction(cls);
4667 entry->AddInstruction(new_inst);
4668 entry->AddInstruction(if_inst);
4669 ManuallyBuildEnvFor(cls, {});
4670 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4671
4672 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4673 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
4674 HInstruction* call2_left = MakeInvoke(DataType::Type::kVoid, {});
4675 HInstruction* goto_left = new (GetAllocator()) HGoto();
4676 left->AddInstruction(call_left);
4677 left->AddInstruction(write_left);
4678 left->AddInstruction(call2_left);
4679 left->AddInstruction(goto_left);
4680 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4681 call2_left->CopyEnvironmentFrom(cls->GetEnvironment());
4682
4683 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4684 HInstruction* call_right = MakeInvoke(DataType::Type::kVoid, {});
4685 HInstruction* goto_right = new (GetAllocator()) HGoto();
4686 right->AddInstruction(write_right);
4687 right->AddInstruction(call_right);
4688 right->AddInstruction(goto_right);
4689 call_right->CopyEnvironmentFrom(cls->GetEnvironment());
4690
4691 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4692 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
4693 breturn->AddInstruction(read_bottom);
4694 breturn->AddInstruction(return_exit);
4695
4696 SetupExit(exit);
4697
4698 // PerformLSE expects this to be empty.
4699 graph_->ClearDominanceInformation();
4700 PerformLSENoPartial();
4701
4702 EXPECT_INS_RETAINED(read_bottom);
4703 EXPECT_INS_RETAINED(write_right);
4704 EXPECT_INS_RETAINED(write_left);
4705 EXPECT_INS_RETAINED(call_left);
4706 EXPECT_INS_RETAINED(call_right);
4707 }
4708
4709 // // ENTRY
4710 // obj = new Obj();
4711 // DO NOT ELIMINATE. Kept by escape.
4712 // obj.field = 3;
4713 // noescape();
4714 // if (parameter_value) {
4715 // // LEFT
4716 // // DO NOT ELIMINATE
4717 // escape(obj);
4718 // obj.field = 1;
4719 // } else {
4720 // // RIGHT
4721 // // ELIMINATE
4722 // obj.field = 2;
4723 // }
4724 // EXIT
4725 // ELIMINATE
4726 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialLoadPreserved6)4727 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved6) {
4728 CreateGraph();
4729 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4730 "exit",
4731 {{"entry", "left"},
4732 {"entry", "right"},
4733 {"left", "breturn"},
4734 {"right", "breturn"},
4735 {"breturn", "exit"}}));
4736 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4737 GET_BLOCK(entry);
4738 GET_BLOCK(exit);
4739 GET_BLOCK(breturn);
4740 GET_BLOCK(left);
4741 GET_BLOCK(right);
4742 #undef GET_BLOCK
4743 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4744 HInstruction* c1 = graph_->GetIntConstant(1);
4745 HInstruction* c2 = graph_->GetIntConstant(2);
4746 HInstruction* c3 = graph_->GetIntConstant(3);
4747
4748 HInstruction* cls = MakeClassLoad();
4749 HInstruction* new_inst = MakeNewInstance(cls);
4750 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
4751 HInstruction* call_entry = MakeInvoke(DataType::Type::kVoid, {});
4752 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4753 entry->AddInstruction(cls);
4754 entry->AddInstruction(new_inst);
4755 entry->AddInstruction(write_entry);
4756 entry->AddInstruction(call_entry);
4757 entry->AddInstruction(if_inst);
4758 ManuallyBuildEnvFor(cls, {});
4759 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4760 call_entry->CopyEnvironmentFrom(cls->GetEnvironment());
4761
4762 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4763 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
4764 HInstruction* goto_left = new (GetAllocator()) HGoto();
4765 left->AddInstruction(call_left);
4766 left->AddInstruction(write_left);
4767 left->AddInstruction(goto_left);
4768 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4769
4770 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4771 HInstruction* goto_right = new (GetAllocator()) HGoto();
4772 right->AddInstruction(write_right);
4773 right->AddInstruction(goto_right);
4774
4775 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4776 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
4777 breturn->AddInstruction(read_bottom);
4778 breturn->AddInstruction(return_exit);
4779
4780 SetupExit(exit);
4781
4782 // PerformLSE expects this to be empty.
4783 graph_->ClearDominanceInformation();
4784
4785 LOG(INFO) << "Pre LSE " << blks;
4786 PerformLSENoPartial();
4787 LOG(INFO) << "Post LSE " << blks;
4788
4789 EXPECT_INS_REMOVED(read_bottom);
4790 EXPECT_INS_REMOVED(write_right);
4791 EXPECT_INS_RETAINED(write_entry);
4792 EXPECT_INS_RETAINED(write_left);
4793 EXPECT_INS_RETAINED(call_left);
4794 EXPECT_INS_RETAINED(call_entry);
4795 }
4796
4797 // // ENTRY
4798 // // MOVED TO MATERIALIZATION BLOCK
4799 // obj = new Obj();
4800 // ELIMINATE, moved to materialization block. Kept by escape.
4801 // obj.field = 3;
4802 // // Make sure this graph isn't broken
4803 // if (obj ==/!= (STATIC.VALUE|obj|null)) {
4804 // // partial_BLOCK
4805 // // REMOVE (either from unreachable or normal PHI creation)
4806 // obj.field = 4;
4807 // }
4808 // if (parameter_value) {
4809 // // LEFT
4810 // // DO NOT ELIMINATE
4811 // escape(obj);
4812 // } else {
4813 // // RIGHT
4814 // // ELIMINATE
4815 // obj.field = 2;
4816 // }
4817 // EXIT
4818 // PREDICATED GET
4819 // return obj.field
TEST_P(PartialComparisonTestGroup,PartialComparisonBeforeCohort)4820 TEST_P(PartialComparisonTestGroup, PartialComparisonBeforeCohort) {
4821 ScopedObjectAccess soa(Thread::Current());
4822 VariableSizedHandleScope vshs(soa.Self());
4823 CreateGraph(/*handles=*/&vshs);
4824 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4825 "exit",
4826 {{"entry", "critical_break"},
4827 {"entry", "partial"},
4828 {"partial", "merge"},
4829 {"critical_break", "merge"},
4830 {"merge", "left"},
4831 {"merge", "right"},
4832 {"left", "breturn"},
4833 {"right", "breturn"},
4834 {"breturn", "exit"}}));
4835 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4836 GET_BLOCK(entry);
4837 GET_BLOCK(merge);
4838 GET_BLOCK(partial);
4839 GET_BLOCK(critical_break);
4840 GET_BLOCK(exit);
4841 GET_BLOCK(breturn);
4842 GET_BLOCK(left);
4843 GET_BLOCK(right);
4844 #undef GET_BLOCK
4845 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4846 HInstruction* c2 = graph_->GetIntConstant(2);
4847 HInstruction* c3 = graph_->GetIntConstant(3);
4848 HInstruction* c4 = graph_->GetIntConstant(4);
4849
4850 HInstruction* cls = MakeClassLoad();
4851 HInstruction* new_inst = MakeNewInstance(cls);
4852 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
4853 ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst);
4854 HInstruction* if_inst = new (GetAllocator()) HIf(cmp_instructions.cmp_);
4855 entry->AddInstruction(cls);
4856 entry->AddInstruction(new_inst);
4857 entry->AddInstruction(write_entry);
4858 cmp_instructions.AddSetup(entry);
4859 entry->AddInstruction(cmp_instructions.cmp_);
4860 entry->AddInstruction(if_inst);
4861 ManuallyBuildEnvFor(cls, {});
4862 cmp_instructions.AddEnvironment(cls->GetEnvironment());
4863 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4864
4865 HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32));
4866 HInstruction* goto_partial = new (GetAllocator()) HGoto();
4867 partial->AddInstruction(write_partial);
4868 partial->AddInstruction(goto_partial);
4869
4870 HInstruction* goto_crit_break = new (GetAllocator()) HGoto();
4871 critical_break->AddInstruction(goto_crit_break);
4872
4873 HInstruction* if_merge = new (GetAllocator()) HIf(bool_value);
4874 merge->AddInstruction(if_merge);
4875
4876 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4877 HInstruction* goto_left = new (GetAllocator()) HGoto();
4878 left->AddInstruction(call_left);
4879 left->AddInstruction(goto_left);
4880 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4881
4882 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4883 HInstruction* goto_right = new (GetAllocator()) HGoto();
4884 right->AddInstruction(write_right);
4885 right->AddInstruction(goto_right);
4886
4887 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4888 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
4889 breturn->AddInstruction(read_bottom);
4890 breturn->AddInstruction(return_exit);
4891
4892 SetupExit(exit);
4893
4894 // PerformLSE expects this to be empty.
4895 graph_->ClearDominanceInformation();
4896
4897 LOG(INFO) << "Pre LSE " << blks;
4898 PerformLSEWithPartial();
4899 LOG(INFO) << "Post LSE " << blks;
4900
4901 std::vector<HPhi*> merges;
4902 HPredicatedInstanceFieldGet* pred_get;
4903 HInstanceFieldSet* init_set;
4904 std::tie(pred_get, init_set) =
4905 FindSingleInstructions<HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_);
4906 std::tie(merges) = FindAllInstructions<HPhi>(graph_);
4907 ASSERT_EQ(merges.size(), 3u);
4908 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
4909 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn;
4910 });
4911 HPhi* merge_value_top = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
4912 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn;
4913 });
4914 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
4915 return p->GetType() == DataType::Type::kReference;
4916 });
4917 EXPECT_INS_REMOVED(read_bottom);
4918 EXPECT_INS_REMOVED(write_entry);
4919 EXPECT_INS_REMOVED(write_partial);
4920 EXPECT_INS_RETAINED(call_left);
4921 CheckFinalInstruction(if_inst->InputAt(0), ComparisonPlacement::kBeforeEscape);
4922 EXPECT_INS_EQ(init_set->InputAt(1), merge_value_top);
4923 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
4924 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return);
4925 }
4926
4927 // // ENTRY
4928 // // MOVED TO MATERIALIZATION BLOCK
4929 // obj = new Obj();
4930 // ELIMINATE, moved to materialization block. Kept by escape.
4931 // obj.field = 3;
4932 // // Make sure this graph isn't broken
4933 // if (parameter_value) {
4934 // if (obj ==/!= (STATIC.VALUE|obj|null)) {
4935 // // partial_BLOCK
4936 // obj.field = 4;
4937 // }
4938 // // LEFT
4939 // // DO NOT ELIMINATE
4940 // escape(obj);
4941 // } else {
4942 // // RIGHT
4943 // // ELIMINATE
4944 // obj.field = 2;
4945 // }
4946 // EXIT
4947 // PREDICATED GET
4948 // return obj.field
TEST_P(PartialComparisonTestGroup,PartialComparisonInCohortBeforeEscape)4949 TEST_P(PartialComparisonTestGroup, PartialComparisonInCohortBeforeEscape) {
4950 ScopedObjectAccess soa(Thread::Current());
4951 VariableSizedHandleScope vshs(soa.Self());
4952 CreateGraph(/*handles=*/&vshs);
4953 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4954 "exit",
4955 {{"entry", "left_begin"},
4956 {"left_begin", "partial"},
4957 {"left_begin", "left_crit_break"},
4958 {"left_crit_break", "left"},
4959 {"partial", "left"},
4960 {"entry", "right"},
4961 {"left", "breturn"},
4962 {"right", "breturn"},
4963 {"breturn", "exit"}}));
4964 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4965 GET_BLOCK(entry);
4966 GET_BLOCK(partial);
4967 GET_BLOCK(left_begin);
4968 GET_BLOCK(exit);
4969 GET_BLOCK(breturn);
4970 GET_BLOCK(left);
4971 GET_BLOCK(left_crit_break);
4972 GET_BLOCK(right);
4973 #undef GET_BLOCK
4974 EnsurePredecessorOrder(left, {left_crit_break, partial});
4975 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4976 HInstruction* c2 = graph_->GetIntConstant(2);
4977 HInstruction* c3 = graph_->GetIntConstant(3);
4978 HInstruction* c4 = graph_->GetIntConstant(4);
4979
4980 HInstruction* cls = MakeClassLoad();
4981 HInstruction* new_inst = MakeNewInstance(cls);
4982 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
4983 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4984 entry->AddInstruction(cls);
4985 entry->AddInstruction(new_inst);
4986 entry->AddInstruction(write_entry);
4987 entry->AddInstruction(if_inst);
4988 ManuallyBuildEnvFor(cls, {});
4989 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4990
4991 ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst);
4992 HInstruction* if_left_begin = new (GetAllocator()) HIf(cmp_instructions.cmp_);
4993 cmp_instructions.AddSetup(left_begin);
4994 left_begin->AddInstruction(cmp_instructions.cmp_);
4995 left_begin->AddInstruction(if_left_begin);
4996 cmp_instructions.AddEnvironment(cls->GetEnvironment());
4997
4998 left_crit_break->AddInstruction(new (GetAllocator()) HGoto());
4999
5000 HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32));
5001 HInstruction* goto_partial = new (GetAllocator()) HGoto();
5002 partial->AddInstruction(write_partial);
5003 partial->AddInstruction(goto_partial);
5004
5005 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5006 HInstruction* goto_left = new (GetAllocator()) HGoto();
5007 left->AddInstruction(call_left);
5008 left->AddInstruction(goto_left);
5009 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5010
5011 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5012 HInstruction* goto_right = new (GetAllocator()) HGoto();
5013 right->AddInstruction(write_right);
5014 right->AddInstruction(goto_right);
5015
5016 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
5017 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
5018 breturn->AddInstruction(read_bottom);
5019 breturn->AddInstruction(return_exit);
5020
5021 SetupExit(exit);
5022
5023 // PerformLSE expects this to be empty.
5024 graph_->ClearDominanceInformation();
5025 LOG(INFO) << "Pre LSE " << blks;
5026 PerformLSEWithPartial();
5027 LOG(INFO) << "Post LSE " << blks;
5028
5029 std::vector<HPhi*> merges;
5030 HInstanceFieldSet* init_set =
5031 FindSingleInstruction<HInstanceFieldSet>(graph_, left_begin->GetSinglePredecessor());
5032 HInstanceFieldSet* partial_set = FindSingleInstruction<HInstanceFieldSet>(graph_, partial);
5033 HPredicatedInstanceFieldGet* pred_get =
5034 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_);
5035 std::tie(merges) = FindAllInstructions<HPhi>(graph_);
5036 ASSERT_EQ(merges.size(), 2u);
5037 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5038 return p->GetType() == DataType::Type::kInt32;
5039 });
5040 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
5041 return p->GetType() == DataType::Type::kReference;
5042 });
5043 EXPECT_EQ(merge_value_return->GetBlock(), breturn)
5044 << blks.GetName(merge_value_return->GetBlock());
5045 EXPECT_INS_REMOVED(read_bottom);
5046 EXPECT_INS_REMOVED(write_entry);
5047 EXPECT_INS_RETAINED(write_partial);
5048 EXPECT_INS_RETAINED(call_left);
5049 CheckFinalInstruction(if_left_begin->InputAt(0), ComparisonPlacement::kInEscape);
5050 EXPECT_INS_EQ(init_set->InputAt(1), c3);
5051 EXPECT_INS_EQ(partial_set->InputAt(0), init_set->InputAt(0));
5052 EXPECT_INS_EQ(partial_set->InputAt(1), c4);
5053 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
5054 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return);
5055 }
5056
5057 // // ENTRY
5058 // // MOVED TO MATERIALIZATION BLOCK
5059 // obj = new Obj();
5060 // ELIMINATE, moved to materialization block. Kept by escape.
5061 // obj.field = 3;
5062 // // Make sure this graph isn't broken
5063 // if (parameter_value) {
5064 // // LEFT
5065 // // DO NOT ELIMINATE
5066 // escape(obj);
5067 // } else {
5068 // // RIGHT
5069 // // ELIMINATE
5070 // obj.field = 2;
5071 // }
5072 // if (obj ==/!= (STATIC.VALUE|obj|null)) {
5073 // // partial_BLOCK
5074 // obj.field = 4;
5075 // }
5076 // EXIT
5077 // PREDICATED GET
5078 // return obj.field
TEST_P(PartialComparisonTestGroup,PartialComparisonAfterCohort)5079 TEST_P(PartialComparisonTestGroup, PartialComparisonAfterCohort) {
5080 ScopedObjectAccess soa(Thread::Current());
5081 VariableSizedHandleScope vshs(soa.Self());
5082 CreateGraph(/*handles=*/&vshs);
5083 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5084 "exit",
5085 {{"entry", "left"},
5086 {"entry", "right"},
5087 {"left", "merge"},
5088 {"right", "merge"},
5089 {"merge", "critical_break"},
5090 {"critical_break", "breturn"},
5091 {"merge", "partial"},
5092 {"partial", "breturn"},
5093 {"breturn", "exit"}}));
5094 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5095 GET_BLOCK(entry);
5096 GET_BLOCK(partial);
5097 GET_BLOCK(critical_break);
5098 GET_BLOCK(merge);
5099 GET_BLOCK(exit);
5100 GET_BLOCK(breturn);
5101 GET_BLOCK(left);
5102 GET_BLOCK(right);
5103 #undef GET_BLOCK
5104 EnsurePredecessorOrder(breturn, {critical_break, partial});
5105 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5106 HInstruction* c2 = graph_->GetIntConstant(2);
5107 HInstruction* c3 = graph_->GetIntConstant(3);
5108 HInstruction* c4 = graph_->GetIntConstant(4);
5109
5110 HInstruction* cls = MakeClassLoad();
5111 HInstruction* new_inst = MakeNewInstance(cls);
5112 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5113 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5114 entry->AddInstruction(cls);
5115 entry->AddInstruction(new_inst);
5116 entry->AddInstruction(write_entry);
5117 entry->AddInstruction(if_inst);
5118 ManuallyBuildEnvFor(cls, {});
5119 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5120
5121 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5122 HInstruction* goto_left = new (GetAllocator()) HGoto();
5123 left->AddInstruction(call_left);
5124 left->AddInstruction(goto_left);
5125 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5126
5127 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5128 HInstruction* goto_right = new (GetAllocator()) HGoto();
5129 right->AddInstruction(write_right);
5130 right->AddInstruction(goto_right);
5131
5132 ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst);
5133 HInstruction* if_merge = new (GetAllocator()) HIf(cmp_instructions.cmp_);
5134 cmp_instructions.AddSetup(merge);
5135 merge->AddInstruction(cmp_instructions.cmp_);
5136 merge->AddInstruction(if_merge);
5137 cmp_instructions.AddEnvironment(cls->GetEnvironment());
5138
5139 HInstanceFieldSet* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32));
5140 HInstruction* goto_partial = new (GetAllocator()) HGoto();
5141 partial->AddInstruction(write_partial);
5142 partial->AddInstruction(goto_partial);
5143
5144 HInstruction* goto_crit_break = new (GetAllocator()) HGoto();
5145 critical_break->AddInstruction(goto_crit_break);
5146
5147 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
5148 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
5149 breturn->AddInstruction(read_bottom);
5150 breturn->AddInstruction(return_exit);
5151
5152 SetupExit(exit);
5153
5154 // PerformLSE expects this to be empty.
5155 graph_->ClearDominanceInformation();
5156 LOG(INFO) << "Pre LSE " << blks;
5157 PerformLSEWithPartial();
5158 LOG(INFO) << "Post LSE " << blks;
5159
5160 std::vector<HPhi*> merges;
5161 HInstanceFieldSet* init_set =
5162 FindSingleInstruction<HInstanceFieldSet>(graph_, left->GetSinglePredecessor());
5163 HPredicatedInstanceFieldGet* pred_get =
5164 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_);
5165 std::tie(merges) = FindAllInstructions<HPhi>(graph_);
5166 ASSERT_EQ(merges.size(), 3u);
5167 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5168 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn;
5169 });
5170 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
5171 return p->GetType() == DataType::Type::kReference;
5172 });
5173 EXPECT_INS_REMOVED(read_bottom);
5174 EXPECT_INS_REMOVED(write_entry);
5175 EXPECT_INS_RETAINED(write_partial);
5176 EXPECT_TRUE(write_partial->GetIsPredicatedSet());
5177 EXPECT_INS_RETAINED(call_left);
5178 CheckFinalInstruction(if_merge->InputAt(0), ComparisonPlacement::kAfterEscape);
5179 EXPECT_INS_EQ(init_set->InputAt(1), c3);
5180 ASSERT_TRUE(write_partial->InputAt(0)->IsPhi());
5181 EXPECT_INS_EQ(write_partial->InputAt(0)->AsPhi()->InputAt(0), init_set->InputAt(0));
5182 EXPECT_INS_EQ(write_partial->InputAt(1), c4);
5183 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
5184 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return);
5185 }
5186
5187 // // ENTRY
5188 // // MOVED TO MATERIALIZATION BLOCK
5189 // obj = new Obj();
5190 // ELIMINATE, moved to materialization block. Kept by escape.
5191 // obj.field = 3;
5192 // // Make sure this graph isn't broken
5193 // if (parameter_value) {
5194 // // LEFT
5195 // // DO NOT ELIMINATE
5196 // escape(obj);
5197 // if (obj ==/!= (STATIC.VALUE|obj|null)) {
5198 // // partial_BLOCK
5199 // obj.field = 4;
5200 // }
5201 // } else {
5202 // // RIGHT
5203 // // ELIMINATE
5204 // obj.field = 2;
5205 // }
5206 // EXIT
5207 // PREDICATED GET
5208 // return obj.field
TEST_P(PartialComparisonTestGroup,PartialComparisonInCohortAfterEscape)5209 TEST_P(PartialComparisonTestGroup, PartialComparisonInCohortAfterEscape) {
5210 PartialComparisonKind kind = GetParam();
5211 ScopedObjectAccess soa(Thread::Current());
5212 VariableSizedHandleScope vshs(soa.Self());
5213 CreateGraph(/*handles=*/&vshs);
5214 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5215 "exit",
5216 {{"entry", "left"},
5217 {"left", "partial"},
5218 {"partial", "left_end"},
5219 {"left", "left_crit_break"},
5220 {"left_crit_break", "left_end"},
5221 {"left_end", "breturn"},
5222 {"entry", "right"},
5223 {"right", "breturn"},
5224 {"breturn", "exit"}}));
5225 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5226 GET_BLOCK(entry);
5227 GET_BLOCK(partial);
5228 GET_BLOCK(left_end);
5229 GET_BLOCK(exit);
5230 GET_BLOCK(breturn);
5231 GET_BLOCK(left);
5232 GET_BLOCK(left_crit_break);
5233 GET_BLOCK(right);
5234 #undef GET_BLOCK
5235 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5236 HInstruction* c2 = graph_->GetIntConstant(2);
5237 HInstruction* c3 = graph_->GetIntConstant(3);
5238 HInstruction* c4 = graph_->GetIntConstant(4);
5239
5240 HInstruction* cls = MakeClassLoad();
5241 HInstruction* new_inst = MakeNewInstance(cls);
5242 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5243 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5244 entry->AddInstruction(cls);
5245 entry->AddInstruction(new_inst);
5246 entry->AddInstruction(write_entry);
5247 entry->AddInstruction(if_inst);
5248 ManuallyBuildEnvFor(cls, {});
5249 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5250
5251 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5252 ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst);
5253 HInstruction* if_left = new (GetAllocator()) HIf(cmp_instructions.cmp_);
5254 left->AddInstruction(call_left);
5255 cmp_instructions.AddSetup(left);
5256 left->AddInstruction(cmp_instructions.cmp_);
5257 left->AddInstruction(if_left);
5258 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5259 cmp_instructions.AddEnvironment(cls->GetEnvironment());
5260 if (if_left->AsIf()->IfTrueSuccessor() != partial) {
5261 left->SwapSuccessors();
5262 }
5263
5264 HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32));
5265 HInstruction* goto_partial = new (GetAllocator()) HGoto();
5266 partial->AddInstruction(write_partial);
5267 partial->AddInstruction(goto_partial);
5268
5269 HInstruction* goto_left_crit_break = new (GetAllocator()) HGoto();
5270 left_crit_break->AddInstruction(goto_left_crit_break);
5271
5272 HInstruction* goto_left_end = new (GetAllocator()) HGoto();
5273 left_end->AddInstruction(goto_left_end);
5274
5275 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5276 HInstruction* goto_right = new (GetAllocator()) HGoto();
5277 right->AddInstruction(write_right);
5278 right->AddInstruction(goto_right);
5279
5280 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
5281 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
5282 breturn->AddInstruction(read_bottom);
5283 breturn->AddInstruction(return_exit);
5284
5285 SetupExit(exit);
5286
5287 // PerformLSE expects this to be empty.
5288 graph_->ClearDominanceInformation();
5289
5290 LOG(INFO) << "Pre LSE " << blks;
5291 PerformLSEWithPartial();
5292 LOG(INFO) << "Post LSE " << blks;
5293
5294 std::vector<HPhi*> merges;
5295 std::vector<HInstanceFieldSet*> sets;
5296 HPredicatedInstanceFieldGet* pred_get =
5297 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_);
5298 std::tie(merges, sets) = FindAllInstructions<HPhi, HInstanceFieldSet>(graph_);
5299 ASSERT_EQ(merges.size(), 2u);
5300 ASSERT_EQ(sets.size(), 2u);
5301 HInstanceFieldSet* init_set = FindOrNull(sets.begin(), sets.end(), [&](HInstanceFieldSet* s) {
5302 return s->GetBlock()->GetSingleSuccessor() == left;
5303 });
5304 EXPECT_INS_EQ(init_set->InputAt(1), c3);
5305 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5306 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn;
5307 });
5308 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
5309 return p->GetType() == DataType::Type::kReference;
5310 });
5311 EXPECT_INS_REMOVED(read_bottom);
5312 EXPECT_INS_REMOVED(write_entry);
5313 if (kind.IsPossiblyTrue()) {
5314 EXPECT_INS_RETAINED(write_partial);
5315 EXPECT_TRUE(std::find(sets.begin(), sets.end(), write_partial) != sets.end());
5316 }
5317 EXPECT_INS_RETAINED(call_left);
5318 CheckFinalInstruction(if_left->InputAt(0), ComparisonPlacement::kInEscape);
5319 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
5320 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return);
5321 }
5322
5323 INSTANTIATE_TEST_SUITE_P(
5324 LoadStoreEliminationTest,
5325 PartialComparisonTestGroup,
5326 testing::Values(PartialComparisonKind{PartialComparisonKind::Type::kEquals,
5327 PartialComparisonKind::Target::kNull,
5328 PartialComparisonKind::Position::kLeft},
5329 PartialComparisonKind{PartialComparisonKind::Type::kEquals,
5330 PartialComparisonKind::Target::kNull,
5331 PartialComparisonKind::Position::kRight},
5332 PartialComparisonKind{PartialComparisonKind::Type::kEquals,
5333 PartialComparisonKind::Target::kValue,
5334 PartialComparisonKind::Position::kLeft},
5335 PartialComparisonKind{PartialComparisonKind::Type::kEquals,
5336 PartialComparisonKind::Target::kValue,
5337 PartialComparisonKind::Position::kRight},
5338 PartialComparisonKind{PartialComparisonKind::Type::kEquals,
5339 PartialComparisonKind::Target::kSelf,
5340 PartialComparisonKind::Position::kLeft},
5341 PartialComparisonKind{PartialComparisonKind::Type::kNotEquals,
5342 PartialComparisonKind::Target::kNull,
5343 PartialComparisonKind::Position::kLeft},
5344 PartialComparisonKind{PartialComparisonKind::Type::kNotEquals,
5345 PartialComparisonKind::Target::kNull,
5346 PartialComparisonKind::Position::kRight},
5347 PartialComparisonKind{PartialComparisonKind::Type::kNotEquals,
5348 PartialComparisonKind::Target::kSelf,
5349 PartialComparisonKind::Position::kLeft},
5350 PartialComparisonKind{PartialComparisonKind::Type::kNotEquals,
5351 PartialComparisonKind::Target::kValue,
5352 PartialComparisonKind::Position::kLeft},
5353 PartialComparisonKind{PartialComparisonKind::Type::kNotEquals,
5354 PartialComparisonKind::Target::kValue,
5355 PartialComparisonKind::Position::kRight}));
5356
5357 // // ENTRY
5358 // obj = new Obj();
5359 // if (parameter_value) {
5360 // // LEFT
5361 // escape(obj);
5362 // } else {
5363 // // RIGHT
5364 // // ELIMINATE
5365 // obj.field = 2;
5366 // }
5367 // EXIT
5368 // predicated-ELIMINATE
5369 // obj.field = 3;
TEST_F(LoadStoreEliminationTest,PredicatedStore1)5370 TEST_F(LoadStoreEliminationTest, PredicatedStore1) {
5371 ScopedObjectAccess soa(Thread::Current());
5372 VariableSizedHandleScope vshs(soa.Self());
5373 InitGraph(/*handles=*/&vshs);
5374 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5375 "exit",
5376 {{"entry", "left"},
5377 {"entry", "right"},
5378 {"left", "breturn"},
5379 {"right", "breturn"},
5380 {"breturn", "exit"}}));
5381 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5382 GET_BLOCK(entry);
5383 GET_BLOCK(exit);
5384 GET_BLOCK(breturn);
5385 GET_BLOCK(left);
5386 GET_BLOCK(right);
5387 #undef GET_BLOCK
5388 EnsurePredecessorOrder(breturn, {left, right});
5389 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5390 HInstruction* null_const = graph_->GetNullConstant();
5391 HInstruction* c2 = graph_->GetIntConstant(2);
5392 HInstruction* c3 = graph_->GetIntConstant(3);
5393
5394 HInstruction* cls = MakeClassLoad();
5395 HInstruction* new_inst = MakeNewInstance(cls);
5396 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5397 entry->AddInstruction(cls);
5398 entry->AddInstruction(new_inst);
5399 entry->AddInstruction(if_inst);
5400 ManuallyBuildEnvFor(cls, {});
5401 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5402
5403 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5404 HInstruction* goto_left = new (GetAllocator()) HGoto();
5405 left->AddInstruction(call_left);
5406 left->AddInstruction(goto_left);
5407 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5408
5409 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5410 HInstruction* goto_right = new (GetAllocator()) HGoto();
5411 right->AddInstruction(write_right);
5412 right->AddInstruction(goto_right);
5413
5414 HInstruction* write_bottom = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5415 HInstruction* return_exit = new (GetAllocator()) HReturnVoid();
5416 breturn->AddInstruction(write_bottom);
5417 breturn->AddInstruction(return_exit);
5418
5419 SetupExit(exit);
5420
5421 // PerformLSE expects this to be empty.
5422 graph_->ClearDominanceInformation();
5423
5424 LOG(INFO) << "Pre LSE " << blks;
5425 PerformLSEWithPartial();
5426 LOG(INFO) << "Post LSE " << blks;
5427
5428 EXPECT_INS_RETAINED(write_bottom);
5429 EXPECT_TRUE(write_bottom->AsInstanceFieldSet()->GetIsPredicatedSet());
5430 EXPECT_INS_REMOVED(write_right);
5431 EXPECT_INS_RETAINED(call_left);
5432 HPhi* merge_alloc = FindSingleInstruction<HPhi>(graph_, breturn);
5433 ASSERT_NE(merge_alloc, nullptr);
5434 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc;
5435 EXPECT_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << *merge_alloc << " cls? " << *cls;
5436 EXPECT_EQ(merge_alloc->InputAt(1), null_const);
5437 }
5438
5439 // // ENTRY
5440 // obj = new Obj();
5441 // obj.field = 3;
5442 // if (parameter_value) {
5443 // // LEFT
5444 // escape(obj);
5445 // } else {
5446 // // RIGHT
5447 // // ELIMINATE
5448 // obj.field = 2;
5449 // }
5450 // // MERGE
5451 // if (second_param) {
5452 // // NON_ESCAPE
5453 // obj.field = 1;
5454 // noescape();
5455 // }
5456 // EXIT
5457 // predicated-ELIMINATE
5458 // obj.field = 4;
TEST_F(LoadStoreEliminationTest,PredicatedStore2)5459 TEST_F(LoadStoreEliminationTest, PredicatedStore2) {
5460 ScopedObjectAccess soa(Thread::Current());
5461 VariableSizedHandleScope vshs(soa.Self());
5462 CreateGraph(/*handles=*/&vshs);
5463 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5464 "exit",
5465 {{"entry", "left"},
5466 {"entry", "right"},
5467 {"left", "merge"},
5468 {"right", "merge"},
5469 {"merge", "non_escape"},
5470 {"non_escape", "breturn"},
5471 {"merge", "merge_crit_break"},
5472 {"merge_crit_break", "breturn"},
5473 {"breturn", "exit"}}));
5474 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5475 GET_BLOCK(entry);
5476 GET_BLOCK(exit);
5477 GET_BLOCK(breturn);
5478 GET_BLOCK(left);
5479 GET_BLOCK(right);
5480 GET_BLOCK(merge);
5481 GET_BLOCK(merge_crit_break);
5482 GET_BLOCK(non_escape);
5483 #undef GET_BLOCK
5484 EnsurePredecessorOrder(merge, {left, right});
5485 EnsurePredecessorOrder(breturn, {merge_crit_break, non_escape});
5486 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5487 HInstruction* bool_value2 = MakeParam(DataType::Type::kBool);
5488 HInstruction* null_const = graph_->GetNullConstant();
5489 HInstruction* c1 = graph_->GetIntConstant(3);
5490 HInstruction* c2 = graph_->GetIntConstant(2);
5491 HInstruction* c3 = graph_->GetIntConstant(3);
5492 HInstruction* c4 = graph_->GetIntConstant(4);
5493
5494 HInstruction* cls = MakeClassLoad();
5495 HInstruction* new_inst = MakeNewInstance(cls);
5496 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5497 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5498 entry->AddInstruction(cls);
5499 entry->AddInstruction(new_inst);
5500 entry->AddInstruction(write_entry);
5501 entry->AddInstruction(if_inst);
5502 ManuallyBuildEnvFor(cls, {});
5503 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5504
5505 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5506 HInstruction* goto_left = new (GetAllocator()) HGoto();
5507 left->AddInstruction(call_left);
5508 left->AddInstruction(goto_left);
5509 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5510
5511 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5512 HInstruction* goto_right = new (GetAllocator()) HGoto();
5513 right->AddInstruction(write_right);
5514 right->AddInstruction(goto_right);
5515
5516 HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2);
5517 merge->AddInstruction(merge_if);
5518
5519 merge_crit_break->AddInstruction(new (GetAllocator()) HGoto());
5520
5521 HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32));
5522 HInstruction* non_escape_call = MakeInvoke(DataType::Type::kVoid, {});
5523 HInstruction* non_escape_goto = new (GetAllocator()) HGoto();
5524 non_escape->AddInstruction(write_non_escape);
5525 non_escape->AddInstruction(non_escape_call);
5526 non_escape->AddInstruction(non_escape_goto);
5527 non_escape_call->CopyEnvironmentFrom(cls->GetEnvironment());
5528
5529 HInstruction* write_bottom = MakeIFieldSet(new_inst, c4, MemberOffset(32));
5530 HInstruction* return_exit = new (GetAllocator()) HReturnVoid();
5531 breturn->AddInstruction(write_bottom);
5532 breturn->AddInstruction(return_exit);
5533
5534 SetupExit(exit);
5535
5536 // PerformLSE expects this to be empty.
5537 graph_->ClearDominanceInformation();
5538 LOG(INFO) << "Pre LSE " << blks;
5539 PerformLSEWithPartial();
5540 LOG(INFO) << "Post LSE " << blks;
5541
5542 EXPECT_INS_RETAINED(write_bottom);
5543 EXPECT_TRUE(write_bottom->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_bottom;
5544 EXPECT_INS_REMOVED(write_right);
5545 EXPECT_INS_RETAINED(call_left);
5546 HInstanceFieldSet* pred_set = FindSingleInstruction<HInstanceFieldSet>(graph_, breturn);
5547 HPhi* merge_alloc = FindSingleInstruction<HPhi>(graph_);
5548 ASSERT_NE(merge_alloc, nullptr);
5549 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc;
5550 EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << " phi is: " << *merge_alloc;
5551 EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const);
5552 ASSERT_NE(pred_set, nullptr);
5553 EXPECT_TRUE(pred_set->GetIsPredicatedSet()) << *pred_set;
5554 EXPECT_INS_EQ(pred_set->InputAt(0), merge_alloc);
5555 }
5556
5557 // // ENTRY
5558 // obj = new Obj();
5559 // obj.field = 3;
5560 // if (parameter_value) {
5561 // // LEFT
5562 // escape(obj);
5563 // } else {
5564 // // RIGHT
5565 // // ELIMINATE
5566 // obj.field = 2;
5567 // }
5568 // EXIT
5569 // predicated-ELIMINATE
5570 // return obj.field
TEST_F(LoadStoreEliminationTest,PredicatedLoad1)5571 TEST_F(LoadStoreEliminationTest, PredicatedLoad1) {
5572 ScopedObjectAccess soa(Thread::Current());
5573 VariableSizedHandleScope vshs(soa.Self());
5574 CreateGraph(/*handles=*/&vshs);
5575 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5576 "exit",
5577 {{"entry", "left"},
5578 {"entry", "right"},
5579 {"left", "breturn"},
5580 {"right", "breturn"},
5581 {"breturn", "exit"}}));
5582 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5583 GET_BLOCK(entry);
5584 GET_BLOCK(exit);
5585 GET_BLOCK(breturn);
5586 GET_BLOCK(left);
5587 GET_BLOCK(right);
5588 #undef GET_BLOCK
5589 EnsurePredecessorOrder(breturn, {left, right});
5590 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5591 HInstruction* null_const = graph_->GetNullConstant();
5592 HInstruction* c2 = graph_->GetIntConstant(2);
5593 HInstruction* c3 = graph_->GetIntConstant(3);
5594
5595 HInstruction* cls = MakeClassLoad();
5596 HInstruction* new_inst = MakeNewInstance(cls);
5597 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5598 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5599 entry->AddInstruction(cls);
5600 entry->AddInstruction(new_inst);
5601 entry->AddInstruction(write_entry);
5602 entry->AddInstruction(if_inst);
5603 ManuallyBuildEnvFor(cls, {});
5604 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5605
5606 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5607 HInstruction* goto_left = new (GetAllocator()) HGoto();
5608 left->AddInstruction(call_left);
5609 left->AddInstruction(goto_left);
5610 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5611
5612 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5613 HInstruction* goto_right = new (GetAllocator()) HGoto();
5614 right->AddInstruction(write_right);
5615 right->AddInstruction(goto_right);
5616
5617 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
5618 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
5619 breturn->AddInstruction(read_bottom);
5620 breturn->AddInstruction(return_exit);
5621
5622 SetupExit(exit);
5623
5624 // PerformLSE expects this to be empty.
5625 graph_->ClearDominanceInformation();
5626 LOG(INFO) << "Pre LSE " << blks;
5627 PerformLSEWithPartial();
5628 LOG(INFO) << "Post LSE " << blks;
5629
5630 EXPECT_INS_REMOVED(read_bottom);
5631 EXPECT_INS_REMOVED(write_right);
5632 EXPECT_INS_RETAINED(call_left);
5633 std::vector<HPhi*> merges;
5634 HPredicatedInstanceFieldGet* pred_get =
5635 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
5636 std::tie(merges) = FindAllInstructions<HPhi>(graph_, breturn);
5637 ASSERT_EQ(merges.size(), 2u);
5638 HPhi* merge_value_return = FindOrNull(
5639 merges.begin(), merges.end(), [](HPhi* p) { return p->GetType() == DataType::Type::kInt32; });
5640 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
5641 return p->GetType() == DataType::Type::kReference;
5642 });
5643 ASSERT_NE(merge_alloc, nullptr);
5644 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc;
5645 EXPECT_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << *merge_alloc << " cls? " << *cls;
5646 EXPECT_EQ(merge_alloc->InputAt(1), null_const);
5647 ASSERT_NE(pred_get, nullptr);
5648 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
5649 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return) << " pred-get is: " << *pred_get;
5650 EXPECT_INS_EQ(merge_value_return->InputAt(0), graph_->GetIntConstant(0))
5651 << " merge val is: " << *merge_value_return;
5652 EXPECT_INS_EQ(merge_value_return->InputAt(1), c2) << " merge val is: " << *merge_value_return;
5653 }
5654
5655 // // ENTRY
5656 // obj1 = new Obj1();
5657 // obj2 = new Obj2();
5658 // obj1.field = 3;
5659 // obj2.field = 13;
5660 // if (parameter_value) {
5661 // // LEFT
5662 // escape(obj1);
5663 // escape(obj2);
5664 // } else {
5665 // // RIGHT
5666 // // ELIMINATE
5667 // obj1.field = 2;
5668 // obj2.field = 12;
5669 // }
5670 // EXIT
5671 // predicated-ELIMINATE
5672 // return obj1.field + obj2.field
TEST_F(LoadStoreEliminationTest,MultiPredicatedLoad1)5673 TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad1) {
5674 ScopedObjectAccess soa(Thread::Current());
5675 VariableSizedHandleScope vshs(soa.Self());
5676 CreateGraph(/*handles=*/&vshs);
5677 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5678 "exit",
5679 {{"entry", "left"},
5680 {"entry", "right"},
5681 {"left", "breturn"},
5682 {"right", "breturn"},
5683 {"breturn", "exit"}}));
5684 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5685 GET_BLOCK(entry);
5686 GET_BLOCK(exit);
5687 GET_BLOCK(breturn);
5688 GET_BLOCK(left);
5689 GET_BLOCK(right);
5690 #undef GET_BLOCK
5691 EnsurePredecessorOrder(breturn, {left, right});
5692 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5693 HInstruction* c2 = graph_->GetIntConstant(2);
5694 HInstruction* c3 = graph_->GetIntConstant(3);
5695 HInstruction* c12 = graph_->GetIntConstant(12);
5696 HInstruction* c13 = graph_->GetIntConstant(13);
5697
5698 HInstruction* cls1 = MakeClassLoad();
5699 HInstruction* cls2 = MakeClassLoad();
5700 HInstruction* new_inst1 = MakeNewInstance(cls1);
5701 HInstruction* new_inst2 = MakeNewInstance(cls2);
5702 HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32));
5703 HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32));
5704 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5705 entry->AddInstruction(cls1);
5706 entry->AddInstruction(cls2);
5707 entry->AddInstruction(new_inst1);
5708 entry->AddInstruction(new_inst2);
5709 entry->AddInstruction(write_entry1);
5710 entry->AddInstruction(write_entry2);
5711 entry->AddInstruction(if_inst);
5712 ManuallyBuildEnvFor(cls1, {});
5713 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
5714 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
5715 new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment());
5716
5717 HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 });
5718 HInstruction* call_left2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 });
5719 HInstruction* goto_left = new (GetAllocator()) HGoto();
5720 left->AddInstruction(call_left1);
5721 left->AddInstruction(call_left2);
5722 left->AddInstruction(goto_left);
5723 call_left1->CopyEnvironmentFrom(cls1->GetEnvironment());
5724 call_left2->CopyEnvironmentFrom(cls1->GetEnvironment());
5725
5726 HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32));
5727 HInstruction* write_right2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32));
5728 HInstruction* goto_right = new (GetAllocator()) HGoto();
5729 right->AddInstruction(write_right1);
5730 right->AddInstruction(write_right2);
5731 right->AddInstruction(goto_right);
5732
5733 HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32));
5734 HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32));
5735 HInstruction* combine =
5736 new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2);
5737 HInstruction* return_exit = new (GetAllocator()) HReturn(combine);
5738 breturn->AddInstruction(read_bottom1);
5739 breturn->AddInstruction(read_bottom2);
5740 breturn->AddInstruction(combine);
5741 breturn->AddInstruction(return_exit);
5742
5743 SetupExit(exit);
5744
5745 // PerformLSE expects this to be empty.
5746 graph_->ClearDominanceInformation();
5747 LOG(INFO) << "Pre LSE " << blks;
5748 PerformLSEWithPartial();
5749 LOG(INFO) << "Post LSE " << blks;
5750
5751 EXPECT_INS_REMOVED(read_bottom1);
5752 EXPECT_INS_REMOVED(read_bottom2);
5753 EXPECT_INS_REMOVED(write_right1);
5754 EXPECT_INS_REMOVED(write_right2);
5755 EXPECT_INS_RETAINED(call_left1);
5756 EXPECT_INS_RETAINED(call_left2);
5757 std::vector<HPhi*> merges;
5758 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
5759 std::tie(merges, pred_gets) =
5760 FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn);
5761 ASSERT_EQ(merges.size(), 4u);
5762 ASSERT_EQ(pred_gets.size(), 2u);
5763 HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5764 return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2;
5765 });
5766 HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5767 return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c12;
5768 });
5769 HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5770 return p->GetType() == DataType::Type::kReference &&
5771 p->InputAt(0)->IsNewInstance() &&
5772 p->InputAt(0)->InputAt(0) == cls1;
5773 });
5774 HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5775 return p->GetType() == DataType::Type::kReference &&
5776 p->InputAt(0)->IsNewInstance() &&
5777 p->InputAt(0)->InputAt(0) == cls2;
5778 });
5779 ASSERT_NE(merge_alloc1, nullptr);
5780 ASSERT_NE(merge_alloc2, nullptr);
5781 EXPECT_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant());
5782 EXPECT_EQ(merge_alloc2->InputAt(1), graph_->GetNullConstant());
5783 HPredicatedInstanceFieldGet* pred_get1 =
5784 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
5785 return pg->GetTarget() == merge_alloc1;
5786 });
5787 HPredicatedInstanceFieldGet* pred_get2 =
5788 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
5789 return pg->GetTarget() == merge_alloc2;
5790 });
5791 ASSERT_NE(pred_get1, nullptr);
5792 EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1);
5793 EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1)
5794 << " pred-get is: " << *pred_get1;
5795 EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0))
5796 << " merge val is: " << *merge_value_return1;
5797 EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1;
5798 ASSERT_NE(pred_get2, nullptr);
5799 EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2);
5800 EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2)
5801 << " pred-get is: " << *pred_get2;
5802 EXPECT_INS_EQ(merge_value_return2->InputAt(0), graph_->GetIntConstant(0))
5803 << " merge val is: " << *merge_value_return1;
5804 EXPECT_INS_EQ(merge_value_return2->InputAt(1), c12) << " merge val is: " << *merge_value_return1;
5805 }
5806
5807 // // ENTRY
5808 // obj1 = new Obj1();
5809 // obj2 = new Obj2();
5810 // obj1.field = 3;
5811 // obj2.field = 13;
5812 // if (parameter_value) {
5813 // // LEFT
5814 // escape(obj1);
5815 // // ELIMINATE
5816 // obj2.field = 12;
5817 // } else {
5818 // // RIGHT
5819 // // ELIMINATE
5820 // obj1.field = 2;
5821 // escape(obj2);
5822 // }
5823 // EXIT
5824 // predicated-ELIMINATE
5825 // return obj1.field + obj2.field
TEST_F(LoadStoreEliminationTest,MultiPredicatedLoad2)5826 TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad2) {
5827 ScopedObjectAccess soa(Thread::Current());
5828 VariableSizedHandleScope vshs(soa.Self());
5829 CreateGraph(/*handles=*/&vshs);
5830 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5831 "exit",
5832 {{"entry", "left"},
5833 {"entry", "right"},
5834 {"left", "breturn"},
5835 {"right", "breturn"},
5836 {"breturn", "exit"}}));
5837 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5838 GET_BLOCK(entry);
5839 GET_BLOCK(exit);
5840 GET_BLOCK(breturn);
5841 GET_BLOCK(left);
5842 GET_BLOCK(right);
5843 #undef GET_BLOCK
5844 EnsurePredecessorOrder(breturn, {left, right});
5845 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5846 HInstruction* c2 = graph_->GetIntConstant(2);
5847 HInstruction* c3 = graph_->GetIntConstant(3);
5848 HInstruction* c12 = graph_->GetIntConstant(12);
5849 HInstruction* c13 = graph_->GetIntConstant(13);
5850
5851 HInstruction* cls1 = MakeClassLoad();
5852 HInstruction* cls2 = MakeClassLoad();
5853 HInstruction* new_inst1 = MakeNewInstance(cls1);
5854 HInstruction* new_inst2 = MakeNewInstance(cls2);
5855 HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32));
5856 HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32));
5857 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5858 entry->AddInstruction(cls1);
5859 entry->AddInstruction(cls2);
5860 entry->AddInstruction(new_inst1);
5861 entry->AddInstruction(new_inst2);
5862 entry->AddInstruction(write_entry1);
5863 entry->AddInstruction(write_entry2);
5864 entry->AddInstruction(if_inst);
5865 ManuallyBuildEnvFor(cls1, {});
5866 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
5867 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
5868 new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment());
5869
5870 HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 });
5871 HInstruction* write_left2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32));
5872 HInstruction* goto_left = new (GetAllocator()) HGoto();
5873 left->AddInstruction(call_left1);
5874 left->AddInstruction(write_left2);
5875 left->AddInstruction(goto_left);
5876 call_left1->CopyEnvironmentFrom(cls1->GetEnvironment());
5877
5878 HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32));
5879 HInstruction* call_right2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 });
5880 HInstruction* goto_right = new (GetAllocator()) HGoto();
5881 right->AddInstruction(write_right1);
5882 right->AddInstruction(call_right2);
5883 right->AddInstruction(goto_right);
5884 call_right2->CopyEnvironmentFrom(cls1->GetEnvironment());
5885
5886 HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32));
5887 HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32));
5888 HInstruction* combine =
5889 new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2);
5890 HInstruction* return_exit = new (GetAllocator()) HReturn(combine);
5891 breturn->AddInstruction(read_bottom1);
5892 breturn->AddInstruction(read_bottom2);
5893 breturn->AddInstruction(combine);
5894 breturn->AddInstruction(return_exit);
5895
5896 SetupExit(exit);
5897
5898 // PerformLSE expects this to be empty.
5899 graph_->ClearDominanceInformation();
5900 LOG(INFO) << "Pre LSE " << blks;
5901 PerformLSEWithPartial();
5902 LOG(INFO) << "Post LSE " << blks;
5903
5904 EXPECT_INS_REMOVED(read_bottom1);
5905 EXPECT_INS_REMOVED(read_bottom2);
5906 EXPECT_INS_REMOVED(write_right1);
5907 EXPECT_INS_REMOVED(write_left2);
5908 EXPECT_INS_RETAINED(call_left1);
5909 EXPECT_INS_RETAINED(call_right2);
5910 std::vector<HPhi*> merges;
5911 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
5912 std::tie(merges, pred_gets) =
5913 FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn);
5914 ASSERT_EQ(merges.size(), 4u);
5915 ASSERT_EQ(pred_gets.size(), 2u);
5916 HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5917 return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2;
5918 });
5919 HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5920 return p->GetType() == DataType::Type::kInt32 && p->InputAt(0) == c12;
5921 });
5922 HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5923 return p->GetType() == DataType::Type::kReference && p->InputAt(1)->IsNullConstant();
5924 });
5925 HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5926 return p->GetType() == DataType::Type::kReference && p->InputAt(0)->IsNullConstant();
5927 });
5928 ASSERT_NE(merge_alloc1, nullptr);
5929 ASSERT_NE(merge_alloc2, nullptr);
5930 EXPECT_TRUE(merge_alloc1->InputAt(0)->IsNewInstance()) << *merge_alloc1;
5931 EXPECT_INS_EQ(merge_alloc1->InputAt(0)->InputAt(0), cls1) << *merge_alloc1;
5932 EXPECT_INS_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant());
5933 EXPECT_TRUE(merge_alloc2->InputAt(1)->IsNewInstance()) << *merge_alloc2;
5934 EXPECT_INS_EQ(merge_alloc2->InputAt(1)->InputAt(0), cls2) << *merge_alloc2;
5935 EXPECT_INS_EQ(merge_alloc2->InputAt(0), graph_->GetNullConstant());
5936 HPredicatedInstanceFieldGet* pred_get1 =
5937 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
5938 return pg->GetTarget() == merge_alloc1;
5939 });
5940 HPredicatedInstanceFieldGet* pred_get2 =
5941 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
5942 return pg->GetTarget() == merge_alloc2;
5943 });
5944 ASSERT_NE(pred_get1, nullptr);
5945 EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1);
5946 EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1)
5947 << " pred-get is: " << *pred_get1;
5948 EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0))
5949 << " merge val is: " << *merge_value_return1;
5950 EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1;
5951 ASSERT_NE(pred_get2, nullptr);
5952 EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2);
5953 EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2)
5954 << " pred-get is: " << *pred_get2;
5955 EXPECT_INS_EQ(merge_value_return2->InputAt(1), graph_->GetIntConstant(0))
5956 << " merge val is: " << *merge_value_return1;
5957 EXPECT_INS_EQ(merge_value_return2->InputAt(0), c12) << " merge val is: " << *merge_value_return1;
5958 }
5959
5960 // Based on structure seen in `java.util.List
5961 // java.util.Collections.checkedList(java.util.List, java.lang.Class)`
5962 // Incorrect accounting would cause attempts to materialize both obj1 and obj2
5963 // in each of the materialization blocks.
5964 // // ENTRY
5965 // Obj obj;
5966 // if (param1) {
5967 // // needs to be moved after param2 check
5968 // obj1 = new Obj1();
5969 // obj1.foo = 33;
5970 // if (param2) {
5971 // return obj1.foo;
5972 // }
5973 // obj = obj1;
5974 // } else {
5975 // obj2 = new Obj2();
5976 // obj2.foo = 44;
5977 // if (param2) {
5978 // return obj2.foo;
5979 // }
5980 // obj = obj2;
5981 // }
5982 // EXIT
5983 // // obj = PHI[obj1, obj2]
5984 // // NB The phi acts as an escape for both obj1 and obj2 meaning as far as the
5985 // // LSA is concerned the escape frontier is left_crit_break->breturn and
5986 // // right_crit_break->breturn for both even though only one of the objects is
5987 // // actually live at each edge.
5988 // // TODO In the future we really should track liveness through PHIs which would
5989 // // allow us to entirely remove the allocation in this test.
5990 // return obj.foo;
TEST_F(LoadStoreEliminationTest,MultiPredicatedLoad3)5991 TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad3) {
5992 ScopedObjectAccess soa(Thread::Current());
5993 VariableSizedHandleScope vshs(soa.Self());
5994 CreateGraph(/*handles=*/&vshs);
5995 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5996 "exit",
5997 {{"entry", "left"},
5998 {"left", "left_end"},
5999 {"left_end", "breturn"},
6000 {"left", "left_exit_early"},
6001 {"left_exit_early", "exit"},
6002 {"entry", "right"},
6003 {"right", "right_end"},
6004 {"right_end", "breturn"},
6005 {"right", "right_exit_early"},
6006 {"right_exit_early", "exit"},
6007 {"breturn", "exit"}}));
6008 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6009 GET_BLOCK(entry);
6010 GET_BLOCK(exit);
6011 GET_BLOCK(breturn);
6012 GET_BLOCK(left);
6013 GET_BLOCK(left_end);
6014 GET_BLOCK(left_exit_early);
6015 GET_BLOCK(right);
6016 GET_BLOCK(right_end);
6017 GET_BLOCK(right_exit_early);
6018 #undef GET_BLOCK
6019 EnsurePredecessorOrder(breturn, {left_end, right_end});
6020 HInstruction* param1 = MakeParam(DataType::Type::kBool);
6021 HInstruction* param2 = MakeParam(DataType::Type::kBool);
6022 HInstruction* c33 = graph_->GetIntConstant(33);
6023 HInstruction* c44 = graph_->GetIntConstant(44);
6024
6025 HInstruction* if_inst = new (GetAllocator()) HIf(param1);
6026 entry->AddInstruction(if_inst);
6027
6028 HInstruction* cls1 = MakeClassLoad();
6029 HInstruction* new_inst1 = MakeNewInstance(cls1);
6030 HInstruction* write1 = MakeIFieldSet(new_inst1, c33, MemberOffset(32));
6031 HInstruction* if_left = new (GetAllocator()) HIf(param2);
6032 left->AddInstruction(cls1);
6033 left->AddInstruction(new_inst1);
6034 left->AddInstruction(write1);
6035 left->AddInstruction(if_left);
6036 ManuallyBuildEnvFor(cls1, {});
6037 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
6038
6039 left_end->AddInstruction(new (GetAllocator()) HGoto());
6040
6041 HInstruction* early_exit_left_read =
6042 MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32));
6043 HInstruction* early_exit_left_return = new (GetAllocator()) HReturn(early_exit_left_read);
6044 left_exit_early->AddInstruction(early_exit_left_read);
6045 left_exit_early->AddInstruction(early_exit_left_return);
6046
6047 HInstruction* cls2 = MakeClassLoad();
6048 HInstruction* new_inst2 = MakeNewInstance(cls2);
6049 HInstruction* write2 = MakeIFieldSet(new_inst2, c44, MemberOffset(32));
6050 HInstruction* if_right = new (GetAllocator()) HIf(param2);
6051 right->AddInstruction(cls2);
6052 right->AddInstruction(new_inst2);
6053 right->AddInstruction(write2);
6054 right->AddInstruction(if_right);
6055 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
6056 new_inst2->CopyEnvironmentFrom(cls2->GetEnvironment());
6057
6058 right_end->AddInstruction(new (GetAllocator()) HGoto());
6059
6060 HInstruction* early_exit_right_read =
6061 MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32));
6062 HInstruction* early_exit_right_return = new (GetAllocator()) HReturn(early_exit_right_read);
6063 right_exit_early->AddInstruction(early_exit_right_read);
6064 right_exit_early->AddInstruction(early_exit_right_return);
6065
6066 HPhi* bottom_phi = MakePhi({new_inst1, new_inst2});
6067 HInstruction* read_bottom = MakeIFieldGet(bottom_phi, DataType::Type::kInt32, MemberOffset(32));
6068 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
6069 breturn->AddPhi(bottom_phi);
6070 breturn->AddInstruction(read_bottom);
6071 breturn->AddInstruction(return_exit);
6072
6073 SetupExit(exit);
6074
6075 // PerformLSE expects this to be empty.
6076 graph_->ClearDominanceInformation();
6077 LOG(INFO) << "Pre LSE " << blks;
6078 PerformLSEWithPartial();
6079 LOG(INFO) << "Post LSE " << blks;
6080
6081 EXPECT_INS_REMOVED(early_exit_left_read);
6082 EXPECT_INS_REMOVED(early_exit_right_read);
6083 EXPECT_INS_RETAINED(bottom_phi);
6084 EXPECT_INS_RETAINED(read_bottom);
6085 EXPECT_INS_EQ(early_exit_left_return->InputAt(0), c33);
6086 EXPECT_INS_EQ(early_exit_right_return->InputAt(0), c44);
6087 // These assert there is only 1 HNewInstance in the given blocks.
6088 HNewInstance* moved_ni1 =
6089 FindSingleInstruction<HNewInstance>(graph_, left_end->GetSinglePredecessor());
6090 HNewInstance* moved_ni2 =
6091 FindSingleInstruction<HNewInstance>(graph_, right_end->GetSinglePredecessor());
6092 ASSERT_NE(moved_ni1, nullptr);
6093 ASSERT_NE(moved_ni2, nullptr);
6094 EXPECT_INS_EQ(bottom_phi->InputAt(0), moved_ni1);
6095 EXPECT_INS_EQ(bottom_phi->InputAt(1), moved_ni2);
6096 }
6097
6098 // // ENTRY
6099 // obj = new Obj();
6100 // if (param1) {
6101 // obj.field = 3;
6102 // noescape();
6103 // } else {
6104 // obj.field = 2;
6105 // noescape();
6106 // }
6107 // int abc;
6108 // if (parameter_value) {
6109 // // LEFT
6110 // abc = 4;
6111 // escape(obj);
6112 // } else {
6113 // // RIGHT
6114 // // ELIMINATE
6115 // noescape();
6116 // abc = obj.field + 4;
6117 // }
6118 // abc = phi
6119 // EXIT
6120 // predicated-ELIMINATE
6121 // return obj.field + abc
TEST_F(LoadStoreEliminationTest,PredicatedLoad4)6122 TEST_F(LoadStoreEliminationTest, PredicatedLoad4) {
6123 ScopedObjectAccess soa(Thread::Current());
6124 VariableSizedHandleScope vshs(soa.Self());
6125 CreateGraph(/*handles=*/&vshs);
6126 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6127 "exit",
6128 {{"entry", "start_left"},
6129 {"entry", "start_right"},
6130 {"start_left", "mid"},
6131 {"start_right", "mid"},
6132 {"mid", "left"},
6133 {"mid", "right"},
6134 {"left", "breturn"},
6135 {"right", "breturn"},
6136 {"breturn", "exit"}}));
6137 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6138 GET_BLOCK(entry);
6139 GET_BLOCK(exit);
6140 GET_BLOCK(breturn);
6141 GET_BLOCK(left);
6142 GET_BLOCK(right);
6143 GET_BLOCK(mid);
6144 GET_BLOCK(start_left);
6145 GET_BLOCK(start_right);
6146 #undef GET_BLOCK
6147 EnsurePredecessorOrder(breturn, {left, right});
6148 EnsurePredecessorOrder(mid, {start_left, start_right});
6149 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
6150 HInstruction* bool_value2 = MakeParam(DataType::Type::kBool);
6151 HInstruction* null_const = graph_->GetNullConstant();
6152 HInstruction* c2 = graph_->GetIntConstant(2);
6153 HInstruction* c3 = graph_->GetIntConstant(3);
6154 HInstruction* c4 = graph_->GetIntConstant(4);
6155
6156 HInstruction* cls = MakeClassLoad();
6157 HInstruction* new_inst = MakeNewInstance(cls);
6158 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
6159 entry->AddInstruction(cls);
6160 entry->AddInstruction(new_inst);
6161 entry->AddInstruction(if_inst);
6162 ManuallyBuildEnvFor(cls, {});
6163 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6164
6165 HInstruction* write_start_left = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6166 HInstruction* call_start_left = MakeInvoke(DataType::Type::kVoid, { });
6167 start_left->AddInstruction(write_start_left);
6168 start_left->AddInstruction(call_start_left);
6169 start_left->AddInstruction(new (GetAllocator()) HGoto());
6170 call_start_left->CopyEnvironmentFrom(cls->GetEnvironment());
6171
6172 HInstruction* write_start_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
6173 HInstruction* call_start_right = MakeInvoke(DataType::Type::kVoid, { });
6174 start_right->AddInstruction(write_start_right);
6175 start_right->AddInstruction(call_start_right);
6176 start_right->AddInstruction(new (GetAllocator()) HGoto());
6177 call_start_right->CopyEnvironmentFrom(cls->GetEnvironment());
6178
6179 mid->AddInstruction(new (GetAllocator()) HIf(bool_value2));
6180
6181 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
6182 HInstruction* goto_left = new (GetAllocator()) HGoto();
6183 left->AddInstruction(call_left);
6184 left->AddInstruction(goto_left);
6185 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
6186
6187 HInstruction* call_right = MakeInvoke(DataType::Type::kVoid, { });
6188 HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
6189 HInstruction* add_right = new (GetAllocator()) HAdd(DataType::Type::kInt32, read_right, c4);
6190 HInstruction* goto_right = new (GetAllocator()) HGoto();
6191 right->AddInstruction(call_right);
6192 right->AddInstruction(read_right);
6193 right->AddInstruction(add_right);
6194 right->AddInstruction(goto_right);
6195 call_right->CopyEnvironmentFrom(cls->GetEnvironment());
6196
6197 HPhi* phi_bottom = MakePhi({c4, add_right});
6198 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
6199 HInstruction* add_bottom =
6200 new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom, phi_bottom);
6201 HInstruction* return_exit = new (GetAllocator()) HReturn(add_bottom);
6202 breturn->AddPhi(phi_bottom);
6203 breturn->AddInstruction(read_bottom);
6204 breturn->AddInstruction(add_bottom);
6205 breturn->AddInstruction(return_exit);
6206
6207 SetupExit(exit);
6208
6209 // PerformLSE expects this to be empty.
6210 graph_->ClearDominanceInformation();
6211 LOG(INFO) << "Pre LSE " << blks;
6212 PerformLSEWithPartial();
6213 LOG(INFO) << "Post LSE " << blks;
6214
6215 EXPECT_INS_REMOVED(read_bottom);
6216 EXPECT_INS_REMOVED(read_right);
6217 EXPECT_INS_RETAINED(call_left);
6218 EXPECT_INS_RETAINED(call_right);
6219 EXPECT_INS_RETAINED(call_start_left);
6220 EXPECT_INS_RETAINED(call_start_right);
6221 std::vector<HPhi*> merges;
6222 HPredicatedInstanceFieldGet* pred_get =
6223 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
6224 std::tie(merges) = FindAllInstructions<HPhi>(graph_, breturn);
6225 ASSERT_EQ(merges.size(), 3u);
6226 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6227 return p != phi_bottom && p->GetType() == DataType::Type::kInt32;
6228 });
6229 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
6230 return p->GetType() == DataType::Type::kReference;
6231 });
6232 ASSERT_NE(merge_alloc, nullptr);
6233 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc;
6234 EXPECT_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << *merge_alloc << " cls? " << *cls;
6235 EXPECT_EQ(merge_alloc->InputAt(1), null_const);
6236 ASSERT_NE(pred_get, nullptr);
6237 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
6238 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return) << " pred-get is: " << *pred_get;
6239 EXPECT_INS_EQ(merge_value_return->InputAt(0), graph_->GetIntConstant(0))
6240 << " merge val is: " << *merge_value_return;
6241 EXPECT_INS_EQ(merge_value_return->InputAt(1), FindSingleInstruction<HPhi>(graph_, mid))
6242 << " merge val is: " << *merge_value_return;
6243 }
6244
6245 // Based on structure seen in `java.util.Set java.util.Collections$UnmodifiableMap.entrySet()`
6246 // We end up having to update a PHI generated by normal LSE.
6247 // // ENTRY
6248 // Obj obj_init = param_obj.BAR;
6249 // if (param1) {
6250 // Obj other = new Obj();
6251 // other.foo = 42;
6252 // if (param2) {
6253 // return other.foo;
6254 // } else {
6255 // param_obj.BAR = other;
6256 // }
6257 // } else { }
6258 // EXIT
6259 // LSE Turns this into PHI[obj_init, other]
6260 // read_bottom = param_obj.BAR;
6261 // // won't be changed. The escape happens with .BAR set so this is in escaping cohort.
6262 // return read_bottom.foo;
TEST_F(LoadStoreEliminationTest,MultiPredicatedLoad4)6263 TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad4) {
6264 ScopedObjectAccess soa(Thread::Current());
6265 VariableSizedHandleScope vshs(soa.Self());
6266 CreateGraph(/*handles=*/&vshs);
6267 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6268 "exit",
6269 {{"entry", "left"},
6270 {"left", "left_early_return"},
6271 {"left_early_return", "exit"},
6272 {"left", "left_write_escape"},
6273 {"left_write_escape", "breturn"},
6274 {"entry", "right"},
6275 {"right", "breturn"},
6276 {"breturn", "exit"}}));
6277 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6278 GET_BLOCK(entry);
6279 GET_BLOCK(exit);
6280 GET_BLOCK(breturn);
6281 GET_BLOCK(left);
6282 GET_BLOCK(left_early_return);
6283 GET_BLOCK(left_write_escape);
6284 GET_BLOCK(right);
6285 #undef GET_BLOCK
6286 MemberOffset foo_offset = MemberOffset(32);
6287 MemberOffset bar_offset = MemberOffset(20);
6288 EnsurePredecessorOrder(breturn, {left_write_escape, right});
6289 HInstruction* c42 = graph_->GetIntConstant(42);
6290 HInstruction* param1 = MakeParam(DataType::Type::kBool);
6291 HInstruction* param2 = MakeParam(DataType::Type::kBool);
6292 HInstruction* param_obj = MakeParam(DataType::Type::kReference);
6293
6294 HInstruction* get_initial = MakeIFieldGet(param_obj, DataType::Type::kReference, bar_offset);
6295 HInstruction* if_inst = new (GetAllocator()) HIf(param1);
6296 entry->AddInstruction(get_initial);
6297 entry->AddInstruction(if_inst);
6298
6299 HInstruction* cls1 = MakeClassLoad();
6300 HInstruction* new_inst1 = MakeNewInstance(cls1);
6301 HInstruction* write1 = MakeIFieldSet(new_inst1, c42, foo_offset);
6302 HInstruction* if_left = new (GetAllocator()) HIf(param2);
6303 left->AddInstruction(cls1);
6304 left->AddInstruction(new_inst1);
6305 left->AddInstruction(write1);
6306 left->AddInstruction(if_left);
6307 ManuallyBuildEnvFor(cls1, {});
6308 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
6309
6310 HInstruction* read_early_return = MakeIFieldGet(new_inst1, DataType::Type::kInt32, foo_offset);
6311 HInstruction* return_early = new (GetAllocator()) HReturn(read_early_return);
6312 left_early_return->AddInstruction(read_early_return);
6313 left_early_return->AddInstruction(return_early);
6314
6315 HInstruction* write_escape = MakeIFieldSet(param_obj, new_inst1, bar_offset);
6316 HInstruction* write_goto = new (GetAllocator()) HGoto();
6317 left_write_escape->AddInstruction(write_escape);
6318 left_write_escape->AddInstruction(write_goto);
6319
6320 right->AddInstruction(new (GetAllocator()) HGoto());
6321
6322 HInstruction* read_bottom = MakeIFieldGet(param_obj, DataType::Type::kReference, bar_offset);
6323 HInstruction* final_read = MakeIFieldGet(read_bottom, DataType::Type::kInt32, foo_offset);
6324 HInstruction* return_exit = new (GetAllocator()) HReturn(final_read);
6325 breturn->AddInstruction(read_bottom);
6326 breturn->AddInstruction(final_read);
6327 breturn->AddInstruction(return_exit);
6328
6329 SetupExit(exit);
6330
6331 // PerformLSE expects this to be empty.
6332 graph_->ClearDominanceInformation();
6333 LOG(INFO) << "Pre LSE " << blks;
6334 PerformLSEWithPartial();
6335 LOG(INFO) << "Post LSE " << blks;
6336
6337 EXPECT_INS_REMOVED(read_bottom);
6338 EXPECT_INS_REMOVED(read_early_return);
6339 EXPECT_INS_EQ(return_early->InputAt(0), c42);
6340 EXPECT_INS_RETAINED(final_read);
6341 HNewInstance* moved_ni =
6342 FindSingleInstruction<HNewInstance>(graph_, left_write_escape->GetSinglePredecessor());
6343 EXPECT_TRUE(final_read->InputAt(0)->IsPhi());
6344 EXPECT_INS_EQ(final_read->InputAt(0)->InputAt(0), moved_ni);
6345 EXPECT_INS_EQ(final_read->InputAt(0)->InputAt(1), get_initial);
6346 }
6347
6348 // // ENTRY
6349 // obj = new Obj();
6350 // obj.field = 3;
6351 // if (parameter_value) {
6352 // // LEFT
6353 // escape(obj);
6354 // } else {
6355 // // RIGHT
6356 // // ELIMINATE
6357 // obj.field = 2;
6358 // }
6359 // // MERGE
6360 // if (second_param) {
6361 // // NON_ESCAPE
6362 // obj.field = 1;
6363 // noescape();
6364 // }
6365 // EXIT
6366 // predicated-ELIMINATE
6367 // return obj.field
TEST_F(LoadStoreEliminationTest,PredicatedLoad2)6368 TEST_F(LoadStoreEliminationTest, PredicatedLoad2) {
6369 ScopedObjectAccess soa(Thread::Current());
6370 VariableSizedHandleScope vshs(soa.Self());
6371 CreateGraph(/*handles=*/&vshs);
6372 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6373 "exit",
6374 {{"entry", "left"},
6375 {"entry", "right"},
6376 {"left", "merge"},
6377 {"right", "merge"},
6378 {"merge", "non_escape"},
6379 {"non_escape", "breturn"},
6380 {"merge", "crit_break"},
6381 {"crit_break", "breturn"},
6382 {"breturn", "exit"}}));
6383 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6384 GET_BLOCK(entry);
6385 GET_BLOCK(exit);
6386 GET_BLOCK(breturn);
6387 GET_BLOCK(left);
6388 GET_BLOCK(right);
6389 GET_BLOCK(merge);
6390 GET_BLOCK(non_escape);
6391 GET_BLOCK(crit_break);
6392 #undef GET_BLOCK
6393 EnsurePredecessorOrder(merge, {left, right});
6394 EnsurePredecessorOrder(breturn, {crit_break, non_escape});
6395 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
6396 HInstruction* bool_value2 = MakeParam(DataType::Type::kBool);
6397 HInstruction* null_const = graph_->GetNullConstant();
6398 HInstruction* c1 = graph_->GetIntConstant(1);
6399 HInstruction* c2 = graph_->GetIntConstant(2);
6400 HInstruction* c3 = graph_->GetIntConstant(3);
6401
6402 HInstruction* cls = MakeClassLoad();
6403 HInstruction* new_inst = MakeNewInstance(cls);
6404 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6405 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
6406 entry->AddInstruction(cls);
6407 entry->AddInstruction(new_inst);
6408 entry->AddInstruction(write_entry);
6409 entry->AddInstruction(if_inst);
6410 ManuallyBuildEnvFor(cls, {});
6411 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6412
6413 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
6414 HInstruction* goto_left = new (GetAllocator()) HGoto();
6415 left->AddInstruction(call_left);
6416 left->AddInstruction(goto_left);
6417 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
6418
6419 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
6420 HInstruction* goto_right = new (GetAllocator()) HGoto();
6421 right->AddInstruction(write_right);
6422 right->AddInstruction(goto_right);
6423
6424 HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2);
6425 merge->AddInstruction(merge_if);
6426
6427 crit_break->AddInstruction(new (GetAllocator()) HGoto());
6428
6429 HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32));
6430 HInstruction* non_escape_call = MakeInvoke(DataType::Type::kVoid, {});
6431 HInstruction* non_escape_goto = new (GetAllocator()) HGoto();
6432 non_escape->AddInstruction(write_non_escape);
6433 non_escape->AddInstruction(non_escape_call);
6434 non_escape->AddInstruction(non_escape_goto);
6435 non_escape_call->CopyEnvironmentFrom(cls->GetEnvironment());
6436
6437 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
6438 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
6439 breturn->AddInstruction(read_bottom);
6440 breturn->AddInstruction(return_exit);
6441
6442 SetupExit(exit);
6443
6444 // PerformLSE expects this to be empty.
6445 graph_->ClearDominanceInformation();
6446 LOG(INFO) << "Pre LSE " << blks;
6447 PerformLSEWithPartial();
6448 LOG(INFO) << "Post LSE " << blks;
6449
6450 EXPECT_INS_REMOVED(read_bottom);
6451 EXPECT_INS_REMOVED(write_right);
6452 EXPECT_INS_RETAINED(call_left);
6453 std::vector<HPhi*> merges;
6454 HPredicatedInstanceFieldGet* pred_get =
6455 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
6456 std::tie(merges) = FindAllInstructions<HPhi>(graph_);
6457 ASSERT_EQ(merges.size(), 3u);
6458 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6459 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn;
6460 });
6461 HPhi* merge_value_merge = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6462 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn;
6463 });
6464 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
6465 return p->GetType() == DataType::Type::kReference;
6466 });
6467 ASSERT_NE(merge_alloc, nullptr);
6468 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc;
6469 EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls)
6470 << " phi is: " << merge_alloc->DumpWithArgs();
6471 EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const);
6472 ASSERT_NE(pred_get, nullptr);
6473 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
6474 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return)
6475 << "get is " << pred_get->DumpWithArgs();
6476 EXPECT_INS_EQ(merge_value_return->InputAt(0), merge_value_merge)
6477 << " phi is: " << *merge_value_return;
6478 EXPECT_INS_EQ(merge_value_return->InputAt(1), c1)
6479 << " phi is: " << merge_value_return->DumpWithArgs();
6480 EXPECT_INS_EQ(merge_value_merge->InputAt(0), graph_->GetIntConstant(0))
6481 << " phi is: " << *merge_value_merge;
6482 EXPECT_INS_EQ(merge_value_merge->InputAt(1), c2)
6483 << " phi is: " << merge_value_merge->DumpWithArgs();
6484 }
6485
6486 // // ENTRY
6487 // obj = new Obj();
6488 // obj.field = 3;
6489 // if (parameter_value) {
6490 // // LEFT
6491 // escape(obj);
6492 // } else {
6493 // // RIGHT
6494 // // ELIMINATE
6495 // obj.field = 2;
6496 // }
6497 // // MERGE
6498 // if (second_param) {
6499 // // NON_ESCAPE
6500 // obj.field = 1;
6501 // }
6502 // noescape();
6503 // EXIT
6504 // predicated-ELIMINATE
6505 // return obj.field
TEST_F(LoadStoreEliminationTest,PredicatedLoad3)6506 TEST_F(LoadStoreEliminationTest, PredicatedLoad3) {
6507 ScopedObjectAccess soa(Thread::Current());
6508 VariableSizedHandleScope vshs(soa.Self());
6509 CreateGraph(/*handles=*/&vshs);
6510 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6511 "exit",
6512 {{"entry", "left"},
6513 {"entry", "right"},
6514 {"left", "merge"},
6515 {"right", "merge"},
6516 {"merge", "non_escape"},
6517 {"non_escape", "breturn"},
6518 {"merge", "crit_break"},
6519 {"crit_break", "breturn"},
6520 {"breturn", "exit"}}));
6521 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6522 GET_BLOCK(entry);
6523 GET_BLOCK(exit);
6524 GET_BLOCK(breturn);
6525 GET_BLOCK(left);
6526 GET_BLOCK(right);
6527 GET_BLOCK(merge);
6528 GET_BLOCK(crit_break);
6529 GET_BLOCK(non_escape);
6530 #undef GET_BLOCK
6531 EnsurePredecessorOrder(merge, {left, right});
6532 EnsurePredecessorOrder(breturn, {crit_break, non_escape});
6533 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
6534 HInstruction* bool_value2 = MakeParam(DataType::Type::kBool);
6535 HInstruction* null_const = graph_->GetNullConstant();
6536 HInstruction* c1 = graph_->GetIntConstant(1);
6537 HInstruction* c2 = graph_->GetIntConstant(2);
6538 HInstruction* c3 = graph_->GetIntConstant(3);
6539
6540 HInstruction* cls = MakeClassLoad();
6541 HInstruction* new_inst = MakeNewInstance(cls);
6542 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6543 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
6544 entry->AddInstruction(cls);
6545 entry->AddInstruction(new_inst);
6546 entry->AddInstruction(write_entry);
6547 entry->AddInstruction(if_inst);
6548 ManuallyBuildEnvFor(cls, {});
6549 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6550
6551 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
6552 HInstruction* goto_left = new (GetAllocator()) HGoto();
6553 left->AddInstruction(call_left);
6554 left->AddInstruction(goto_left);
6555 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
6556
6557 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
6558 HInstruction* goto_right = new (GetAllocator()) HGoto();
6559 right->AddInstruction(write_right);
6560 right->AddInstruction(goto_right);
6561
6562 HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2);
6563 merge->AddInstruction(merge_if);
6564
6565 HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32));
6566 HInstruction* non_escape_goto = new (GetAllocator()) HGoto();
6567 non_escape->AddInstruction(write_non_escape);
6568 non_escape->AddInstruction(non_escape_goto);
6569
6570 crit_break->AddInstruction(new (GetAllocator()) HGoto());
6571
6572 HInstruction* bottom_call = MakeInvoke(DataType::Type::kVoid, {});
6573 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
6574 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
6575 breturn->AddInstruction(bottom_call);
6576 breturn->AddInstruction(read_bottom);
6577 breturn->AddInstruction(return_exit);
6578 bottom_call->CopyEnvironmentFrom(cls->GetEnvironment());
6579
6580 SetupExit(exit);
6581
6582 // PerformLSE expects this to be empty.
6583 graph_->ClearDominanceInformation();
6584 LOG(INFO) << "Pre LSE " << blks;
6585 PerformLSEWithPartial();
6586 LOG(INFO) << "Post LSE " << blks;
6587
6588 EXPECT_INS_REMOVED(read_bottom);
6589 EXPECT_INS_REMOVED(write_right);
6590 EXPECT_INS_RETAINED(call_left);
6591 std::vector<HPhi*> merges;
6592 HPredicatedInstanceFieldGet* pred_get =
6593 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
6594 std::tie(merges) = FindAllInstructions<HPhi>(graph_);
6595 ASSERT_EQ(merges.size(), 3u);
6596 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6597 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn;
6598 });
6599 HPhi* merge_value_merge = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6600 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn;
6601 });
6602 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
6603 return p->GetType() == DataType::Type::kReference;
6604 });
6605 ASSERT_NE(merge_alloc, nullptr);
6606 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << merge_alloc->DumpWithArgs();
6607 EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls)
6608 << " phi is: " << merge_alloc->DumpWithArgs();
6609 EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const);
6610 ASSERT_NE(pred_get, nullptr);
6611 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
6612 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return)
6613 << "get is " << pred_get->DumpWithArgs();
6614 EXPECT_INS_EQ(merge_value_return->InputAt(0), merge_value_merge)
6615 << " phi is: " << *merge_value_return;
6616 EXPECT_INS_EQ(merge_value_return->InputAt(1), c1) << " phi is: " << *merge_value_return;
6617 EXPECT_INS_EQ(merge_value_merge->InputAt(0), graph_->GetIntConstant(0))
6618 << " phi is: " << *merge_value_merge;
6619 EXPECT_INS_EQ(merge_value_merge->InputAt(1), c2) << " phi is: " << *merge_value_merge;
6620 }
6621
6622 // // ENTRY
6623 // obj = new Obj();
6624 // if (parameter_value) {
6625 // // LEFT
6626 // obj.field = 3;
6627 // escape(obj);
6628 // } else {
6629 // // RIGHT - Leave it as default value
6630 // }
6631 // EXIT
6632 // predicated-ELIMINATE
6633 // return obj.field
TEST_F(LoadStoreEliminationTest,PredicatedLoadDefaultValue)6634 TEST_F(LoadStoreEliminationTest, PredicatedLoadDefaultValue) {
6635 ScopedObjectAccess soa(Thread::Current());
6636 VariableSizedHandleScope vshs(soa.Self());
6637 CreateGraph(/*handles=*/&vshs);
6638 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6639 "exit",
6640 {{"entry", "left"},
6641 {"entry", "right"},
6642 {"left", "breturn"},
6643 {"right", "breturn"},
6644 {"breturn", "exit"}}));
6645 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6646 GET_BLOCK(entry);
6647 GET_BLOCK(exit);
6648 GET_BLOCK(breturn);
6649 GET_BLOCK(left);
6650 GET_BLOCK(right);
6651 #undef GET_BLOCK
6652 EnsurePredecessorOrder(breturn, {left, right});
6653 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
6654 HInstruction* null_const = graph_->GetNullConstant();
6655 HInstruction* c0 = graph_->GetIntConstant(0);
6656 HInstruction* c3 = graph_->GetIntConstant(3);
6657
6658 HInstruction* cls = MakeClassLoad();
6659 HInstruction* new_inst = MakeNewInstance(cls);
6660 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
6661 entry->AddInstruction(cls);
6662 entry->AddInstruction(new_inst);
6663 entry->AddInstruction(if_inst);
6664 ManuallyBuildEnvFor(cls, {});
6665 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6666
6667 HInstruction* write_left = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6668 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
6669 HInstruction* goto_left = new (GetAllocator()) HGoto();
6670 left->AddInstruction(write_left);
6671 left->AddInstruction(call_left);
6672 left->AddInstruction(goto_left);
6673 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
6674
6675 HInstruction* goto_right = new (GetAllocator()) HGoto();
6676 right->AddInstruction(goto_right);
6677
6678 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
6679 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
6680 breturn->AddInstruction(read_bottom);
6681 breturn->AddInstruction(return_exit);
6682
6683 SetupExit(exit);
6684
6685 // PerformLSE expects this to be empty.
6686 graph_->ClearDominanceInformation();
6687 LOG(INFO) << "Pre LSE " << blks;
6688 PerformLSEWithPartial();
6689 LOG(INFO) << "Post LSE " << blks;
6690
6691 EXPECT_INS_REMOVED(read_bottom);
6692 EXPECT_INS_RETAINED(write_left);
6693 EXPECT_INS_RETAINED(call_left);
6694 HPredicatedInstanceFieldGet* pred_get =
6695 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
6696 HPhi* merge_alloc = FindSingleInstruction<HPhi>(graph_, breturn);
6697 ASSERT_NE(merge_alloc, nullptr);
6698 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc;
6699 EXPECT_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << *merge_alloc << " cls? " << *cls;
6700 EXPECT_EQ(merge_alloc->InputAt(1), null_const);
6701 ASSERT_NE(pred_get, nullptr);
6702 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
6703 EXPECT_INS_EQ(pred_get->GetDefaultValue(), c0) << " pred-get is: " << *pred_get;
6704 }
6705
6706 // // ENTRY
6707 // obj = new Obj();
6708 // // ALL should be kept
6709 // switch (parameter_value) {
6710 // case 1:
6711 // // Case1
6712 // obj.field = 1;
6713 // call_func(obj);
6714 // break;
6715 // case 2:
6716 // // Case2
6717 // obj.field = 2;
6718 // call_func(obj);
6719 // break;
6720 // default:
6721 // // Case3
6722 // obj.field = 3;
6723 // do {
6724 // if (test2()) { } else { obj.field = 5; }
6725 // } while (test());
6726 // break;
6727 // }
6728 // EXIT
6729 // // predicated-ELIMINATE
6730 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialLoopPhis1)6731 TEST_F(LoadStoreEliminationTest, PartialLoopPhis1) {
6732 ScopedObjectAccess soa(Thread::Current());
6733 VariableSizedHandleScope vshs(soa.Self());
6734 CreateGraph(/*handles=*/&vshs);
6735 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6736 "exit",
6737 {{"entry", "bswitch"},
6738 {"bswitch", "case1"},
6739 {"bswitch", "case2"},
6740 {"bswitch", "case3"},
6741 {"case1", "breturn"},
6742 {"case2", "breturn"},
6743 {"case3", "loop_pre_header"},
6744 {"loop_pre_header", "loop_header"},
6745 {"loop_header", "loop_body"},
6746 {"loop_body", "loop_if_left"},
6747 {"loop_body", "loop_if_right"},
6748 {"loop_if_left", "loop_merge"},
6749 {"loop_if_right", "loop_merge"},
6750 {"loop_merge", "loop_end"},
6751 {"loop_end", "loop_header"},
6752 {"loop_end", "critical_break"},
6753 {"critical_break", "breturn"},
6754 {"breturn", "exit"}}));
6755 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6756 GET_BLOCK(entry);
6757 GET_BLOCK(bswitch);
6758 GET_BLOCK(exit);
6759 GET_BLOCK(breturn);
6760 GET_BLOCK(case1);
6761 GET_BLOCK(case2);
6762 GET_BLOCK(case3);
6763
6764 GET_BLOCK(loop_pre_header);
6765 GET_BLOCK(loop_header);
6766 GET_BLOCK(loop_body);
6767 GET_BLOCK(loop_if_left);
6768 GET_BLOCK(loop_if_right);
6769 GET_BLOCK(loop_merge);
6770 GET_BLOCK(loop_end);
6771 GET_BLOCK(critical_break);
6772 #undef GET_BLOCK
6773 EnsurePredecessorOrder(breturn, {case1, case2, critical_break});
6774 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_end});
6775 EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right});
6776 CHECK_SUBROUTINE_FAILURE();
6777 HInstruction* switch_val = MakeParam(DataType::Type::kInt32);
6778 HInstruction* c1 = graph_->GetIntConstant(1);
6779 HInstruction* c2 = graph_->GetIntConstant(2);
6780 HInstruction* c3 = graph_->GetIntConstant(3);
6781 HInstruction* c5 = graph_->GetIntConstant(5);
6782
6783 HInstruction* cls = MakeClassLoad();
6784 HInstruction* new_inst = MakeNewInstance(cls);
6785 HInstruction* entry_goto = new (GetAllocator()) HGoto();
6786 entry->AddInstruction(cls);
6787 entry->AddInstruction(new_inst);
6788 entry->AddInstruction(entry_goto);
6789 ManuallyBuildEnvFor(cls, {});
6790 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6791
6792 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val);
6793 bswitch->AddInstruction(switch_inst);
6794
6795 HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32));
6796 HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst });
6797 HInstruction* goto_c1 = new (GetAllocator()) HGoto();
6798 case1->AddInstruction(write_c1);
6799 case1->AddInstruction(call_c1);
6800 case1->AddInstruction(goto_c1);
6801 call_c1->CopyEnvironmentFrom(cls->GetEnvironment());
6802
6803 HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32));
6804 HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst });
6805 HInstruction* goto_c2 = new (GetAllocator()) HGoto();
6806 case2->AddInstruction(write_c2);
6807 case2->AddInstruction(call_c2);
6808 case2->AddInstruction(goto_c2);
6809 call_c2->CopyEnvironmentFrom(cls->GetEnvironment());
6810
6811 HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6812 HInstruction* goto_c3 = new (GetAllocator()) HGoto();
6813 case3->AddInstruction(write_c3);
6814 case3->AddInstruction(goto_c3);
6815
6816 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
6817 loop_pre_header->AddInstruction(goto_preheader);
6818
6819 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
6820 HInstruction* goto_header = new (GetAllocator()) HGoto();
6821 loop_header->AddInstruction(suspend_check_header);
6822 loop_header->AddInstruction(goto_header);
6823 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
6824
6825 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
6826 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
6827 loop_body->AddInstruction(call_loop_body);
6828 loop_body->AddInstruction(if_loop_body);
6829 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
6830
6831 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
6832 loop_if_left->AddInstruction(goto_loop_left);
6833
6834 HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32));
6835 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
6836 loop_if_right->AddInstruction(write_loop_right);
6837 loop_if_right->AddInstruction(goto_loop_right);
6838
6839 HInstruction* goto_loop_merge = new (GetAllocator()) HGoto();
6840 loop_merge->AddInstruction(goto_loop_merge);
6841
6842 HInstruction* call_end = MakeInvoke(DataType::Type::kBool, {});
6843 HInstruction* if_end = new (GetAllocator()) HIf(call_end);
6844 loop_end->AddInstruction(call_end);
6845 loop_end->AddInstruction(if_end);
6846 call_end->CopyEnvironmentFrom(cls->GetEnvironment());
6847
6848 HInstruction* goto_critical_break = new (GetAllocator()) HGoto();
6849 critical_break->AddInstruction(goto_critical_break);
6850
6851 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
6852 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
6853 breturn->AddInstruction(read_bottom);
6854 breturn->AddInstruction(return_exit);
6855
6856 SetupExit(exit);
6857
6858 // PerformLSE expects this to be empty.
6859 graph_->ClearDominanceInformation();
6860 LOG(INFO) << "Pre LSE " << blks;
6861 PerformLSEWithPartial();
6862 LOG(INFO) << "Post LSE " << blks;
6863
6864 HPredicatedInstanceFieldGet* pred_get =
6865 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
6866 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
6867 ASSERT_TRUE(pred_get != nullptr);
6868 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
6869 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
6870 EXPECT_INS_EQ(inst_return_phi->InputAt(0),
6871 FindSingleInstruction<HNewInstance>(graph_, case1->GetSinglePredecessor()));
6872 EXPECT_INS_EQ(inst_return_phi->InputAt(1),
6873 FindSingleInstruction<HNewInstance>(graph_, case2->GetSinglePredecessor()));
6874 EXPECT_INS_EQ(inst_return_phi->InputAt(2), graph_->GetNullConstant());
6875 HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi();
6876 ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs();
6877 EXPECT_INS_EQ(inst_value_phi->InputAt(0), graph_->GetIntConstant(0));
6878 EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0));
6879 HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge);
6880 ASSERT_TRUE(loop_merge_phi != nullptr);
6881 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
6882 ASSERT_TRUE(loop_header_phi != nullptr);
6883 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
6884 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi);
6885 EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi);
6886 EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5);
6887 EXPECT_INS_EQ(inst_value_phi->InputAt(2), loop_merge_phi);
6888 EXPECT_INS_RETAINED(write_c1) << *write_c1;
6889 EXPECT_INS_RETAINED(write_c2) << *write_c2;
6890 EXPECT_INS_REMOVED(write_c3) << *write_c3;
6891 EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right;
6892 }
6893
6894 // // ENTRY
6895 // obj = new Obj();
6896 // switch (parameter_value) {
6897 // case 1:
6898 // // Case1
6899 // obj.field = 1;
6900 // call_func(obj);
6901 // break;
6902 // case 2:
6903 // // Case2
6904 // obj.field = 2;
6905 // call_func(obj);
6906 // break;
6907 // default:
6908 // // Case3
6909 // obj.field = 3;
6910 // while (!test()) {
6911 // if (test2()) { } else { obj.field = 5; }
6912 // }
6913 // break;
6914 // }
6915 // EXIT
6916 // // predicated-ELIMINATE
6917 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialLoopPhis2)6918 TEST_F(LoadStoreEliminationTest, PartialLoopPhis2) {
6919 ScopedObjectAccess soa(Thread::Current());
6920 VariableSizedHandleScope vshs(soa.Self());
6921 CreateGraph(/*handles=*/&vshs);
6922 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6923 "exit",
6924 {{"entry", "bswitch"},
6925 {"bswitch", "case1"},
6926 {"bswitch", "case2"},
6927 {"bswitch", "case3"},
6928 {"case1", "breturn"},
6929 {"case2", "breturn"},
6930 {"case3", "loop_pre_header"},
6931
6932 {"loop_pre_header", "loop_header"},
6933 {"loop_header", "critical_break"},
6934 {"loop_header", "loop_body"},
6935 {"loop_body", "loop_if_left"},
6936 {"loop_body", "loop_if_right"},
6937 {"loop_if_left", "loop_merge"},
6938 {"loop_if_right", "loop_merge"},
6939 {"loop_merge", "loop_header"},
6940
6941 {"critical_break", "breturn"},
6942 {"breturn", "exit"}}));
6943 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6944 GET_BLOCK(entry);
6945 GET_BLOCK(bswitch);
6946 GET_BLOCK(exit);
6947 GET_BLOCK(breturn);
6948 GET_BLOCK(case1);
6949 GET_BLOCK(case2);
6950 GET_BLOCK(case3);
6951
6952 GET_BLOCK(loop_pre_header);
6953 GET_BLOCK(loop_header);
6954 GET_BLOCK(loop_body);
6955 GET_BLOCK(loop_if_left);
6956 GET_BLOCK(loop_if_right);
6957 GET_BLOCK(loop_merge);
6958 GET_BLOCK(critical_break);
6959 #undef GET_BLOCK
6960 EnsurePredecessorOrder(breturn, {case1, case2, critical_break});
6961 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge});
6962 EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right});
6963 CHECK_SUBROUTINE_FAILURE();
6964 HInstruction* switch_val = MakeParam(DataType::Type::kInt32);
6965 HInstruction* c1 = graph_->GetIntConstant(1);
6966 HInstruction* c2 = graph_->GetIntConstant(2);
6967 HInstruction* c3 = graph_->GetIntConstant(3);
6968 HInstruction* c5 = graph_->GetIntConstant(5);
6969
6970 HInstruction* cls = MakeClassLoad();
6971 HInstruction* new_inst = MakeNewInstance(cls);
6972 HInstruction* entry_goto = new (GetAllocator()) HGoto();
6973 entry->AddInstruction(cls);
6974 entry->AddInstruction(new_inst);
6975 entry->AddInstruction(entry_goto);
6976 ManuallyBuildEnvFor(cls, {});
6977 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6978
6979 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val);
6980 bswitch->AddInstruction(switch_inst);
6981
6982 HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32));
6983 HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst });
6984 HInstruction* goto_c1 = new (GetAllocator()) HGoto();
6985 case1->AddInstruction(write_c1);
6986 case1->AddInstruction(call_c1);
6987 case1->AddInstruction(goto_c1);
6988 call_c1->CopyEnvironmentFrom(cls->GetEnvironment());
6989
6990 HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32));
6991 HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst });
6992 HInstruction* goto_c2 = new (GetAllocator()) HGoto();
6993 case2->AddInstruction(write_c2);
6994 case2->AddInstruction(call_c2);
6995 case2->AddInstruction(goto_c2);
6996 call_c2->CopyEnvironmentFrom(cls->GetEnvironment());
6997
6998 HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6999 HInstruction* goto_c3 = new (GetAllocator()) HGoto();
7000 case3->AddInstruction(write_c3);
7001 case3->AddInstruction(goto_c3);
7002
7003 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
7004 loop_pre_header->AddInstruction(goto_preheader);
7005
7006 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
7007 HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {});
7008 HInstruction* if_header = new (GetAllocator()) HIf(call_header);
7009 loop_header->AddInstruction(suspend_check_header);
7010 loop_header->AddInstruction(call_header);
7011 loop_header->AddInstruction(if_header);
7012 call_header->CopyEnvironmentFrom(cls->GetEnvironment());
7013 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
7014
7015 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
7016 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
7017 loop_body->AddInstruction(call_loop_body);
7018 loop_body->AddInstruction(if_loop_body);
7019 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
7020
7021 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
7022 loop_if_left->AddInstruction(goto_loop_left);
7023
7024 HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32));
7025 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
7026 loop_if_right->AddInstruction(write_loop_right);
7027 loop_if_right->AddInstruction(goto_loop_right);
7028
7029 HInstruction* goto_loop_merge = new (GetAllocator()) HGoto();
7030 loop_merge->AddInstruction(goto_loop_merge);
7031
7032 HInstruction* goto_critical_break = new (GetAllocator()) HGoto();
7033 critical_break->AddInstruction(goto_critical_break);
7034
7035 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7036 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
7037 breturn->AddInstruction(read_bottom);
7038 breturn->AddInstruction(return_exit);
7039
7040 SetupExit(exit);
7041
7042 // PerformLSE expects this to be empty.
7043 graph_->ClearDominanceInformation();
7044 LOG(INFO) << "Pre LSE " << blks;
7045 PerformLSEWithPartial();
7046 LOG(INFO) << "Post LSE " << blks;
7047
7048 HPredicatedInstanceFieldGet* pred_get =
7049 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7050 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
7051 ASSERT_TRUE(pred_get != nullptr);
7052 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
7053 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
7054 EXPECT_INS_EQ(inst_return_phi->InputAt(0),
7055 FindSingleInstruction<HNewInstance>(graph_, case1->GetSinglePredecessor()));
7056 EXPECT_INS_EQ(inst_return_phi->InputAt(1),
7057 FindSingleInstruction<HNewInstance>(graph_, case2->GetSinglePredecessor()));
7058 EXPECT_INS_EQ(inst_return_phi->InputAt(2), graph_->GetNullConstant());
7059 HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi();
7060 ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs();
7061 EXPECT_INS_EQ(inst_value_phi->InputAt(0), graph_->GetIntConstant(0));
7062 EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0));
7063 HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge);
7064 ASSERT_TRUE(loop_merge_phi != nullptr);
7065 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
7066 ASSERT_TRUE(loop_header_phi != nullptr);
7067 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
7068 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi);
7069 EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi);
7070 EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5);
7071 EXPECT_INS_EQ(inst_value_phi->InputAt(2), loop_header_phi);
7072 EXPECT_INS_RETAINED(write_c1) << *write_c1;
7073 EXPECT_INS_RETAINED(write_c2) << *write_c2;
7074 EXPECT_INS_REMOVED(write_c3) << *write_c3;
7075 EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right;
7076 }
7077
7078 // // ENTRY
7079 // obj = new Obj();
7080 // obj.field = 3;
7081 // while (!test()) {
7082 // if (test2()) { } else { obj.field = 5; }
7083 // }
7084 // if (parameter_value) {
7085 // escape(obj);
7086 // }
7087 // EXIT
7088 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialLoopPhis3)7089 TEST_F(LoadStoreEliminationTest, PartialLoopPhis3) {
7090 ScopedObjectAccess soa(Thread::Current());
7091 VariableSizedHandleScope vshs(soa.Self());
7092 CreateGraph(/*handles=*/&vshs);
7093 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7094 "exit",
7095 {{"entry", "loop_pre_header"},
7096
7097 {"loop_pre_header", "loop_header"},
7098 {"loop_header", "escape_check"},
7099 {"loop_header", "loop_body"},
7100 {"loop_body", "loop_if_left"},
7101 {"loop_body", "loop_if_right"},
7102 {"loop_if_left", "loop_merge"},
7103 {"loop_if_right", "loop_merge"},
7104 {"loop_merge", "loop_header"},
7105
7106 {"escape_check", "escape"},
7107 {"escape_check", "no_escape"},
7108 {"no_escape", "breturn"},
7109 {"escape", "breturn"},
7110 {"breturn", "exit"}}));
7111 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7112 GET_BLOCK(entry);
7113 GET_BLOCK(exit);
7114 GET_BLOCK(breturn);
7115 GET_BLOCK(no_escape);
7116 GET_BLOCK(escape);
7117 GET_BLOCK(escape_check);
7118
7119 GET_BLOCK(loop_pre_header);
7120 GET_BLOCK(loop_header);
7121 GET_BLOCK(loop_body);
7122 GET_BLOCK(loop_if_left);
7123 GET_BLOCK(loop_if_right);
7124 GET_BLOCK(loop_merge);
7125 #undef GET_BLOCK
7126 EnsurePredecessorOrder(breturn, {no_escape, escape});
7127 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge});
7128 EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right});
7129 CHECK_SUBROUTINE_FAILURE();
7130 HInstruction* bool_val = MakeParam(DataType::Type::kBool);
7131 HInstruction* c3 = graph_->GetIntConstant(3);
7132 HInstruction* c5 = graph_->GetIntConstant(5);
7133
7134 HInstruction* cls = MakeClassLoad();
7135 HInstruction* new_inst = MakeNewInstance(cls);
7136 HInstruction* entry_goto = new (GetAllocator()) HGoto();
7137 entry->AddInstruction(cls);
7138 entry->AddInstruction(new_inst);
7139 entry->AddInstruction(entry_goto);
7140 ManuallyBuildEnvFor(cls, {});
7141 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7142
7143 HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7144 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
7145 loop_pre_header->AddInstruction(write_pre_header);
7146 loop_pre_header->AddInstruction(goto_preheader);
7147
7148 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
7149 HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {});
7150 HInstruction* if_header = new (GetAllocator()) HIf(call_header);
7151 loop_header->AddInstruction(suspend_check_header);
7152 loop_header->AddInstruction(call_header);
7153 loop_header->AddInstruction(if_header);
7154 call_header->CopyEnvironmentFrom(cls->GetEnvironment());
7155 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
7156
7157 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
7158 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
7159 loop_body->AddInstruction(call_loop_body);
7160 loop_body->AddInstruction(if_loop_body);
7161 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
7162
7163 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
7164 loop_if_left->AddInstruction(goto_loop_left);
7165
7166 HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32));
7167 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
7168 loop_if_right->AddInstruction(write_loop_right);
7169 loop_if_right->AddInstruction(goto_loop_right);
7170
7171 HInstruction* goto_loop_merge = new (GetAllocator()) HGoto();
7172 loop_merge->AddInstruction(goto_loop_merge);
7173
7174 HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val);
7175 escape_check->AddInstruction(if_esc_check);
7176
7177 HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst });
7178 HInstruction* goto_escape = new (GetAllocator()) HGoto();
7179 escape->AddInstruction(call_escape);
7180 escape->AddInstruction(goto_escape);
7181 call_escape->CopyEnvironmentFrom(cls->GetEnvironment());
7182
7183 HInstruction* goto_no_escape = new (GetAllocator()) HGoto();
7184 no_escape->AddInstruction(goto_no_escape);
7185
7186 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7187 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
7188 breturn->AddInstruction(read_bottom);
7189 breturn->AddInstruction(return_exit);
7190
7191 SetupExit(exit);
7192
7193 // PerformLSE expects this to be empty.
7194 graph_->ClearDominanceInformation();
7195 LOG(INFO) << "Pre LSE " << blks;
7196 PerformLSEWithPartial();
7197 LOG(INFO) << "Post LSE " << blks;
7198
7199 HPredicatedInstanceFieldGet* pred_get =
7200 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7201 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
7202 ASSERT_TRUE(pred_get != nullptr);
7203 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
7204 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
7205 EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant());
7206 EXPECT_INS_EQ(inst_return_phi->InputAt(1),
7207 FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor()));
7208 HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi();
7209 ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs();
7210 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
7211 HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge);
7212 EXPECT_INS_EQ(inst_value_phi->InputAt(0), loop_header_phi);
7213 EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0));
7214 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
7215 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi);
7216 EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi);
7217 EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5);
7218 HInstanceFieldSet* mat_set =
7219 FindSingleInstruction<HInstanceFieldSet>(graph_, escape->GetSinglePredecessor());
7220 ASSERT_NE(mat_set, nullptr);
7221 EXPECT_INS_EQ(mat_set->InputAt(1), loop_header_phi);
7222 EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right;
7223 EXPECT_INS_REMOVED(write_pre_header) << *write_pre_header;
7224 }
7225
7226 // // ENTRY
7227 // obj = new Obj();
7228 // if (parameter_value) {
7229 // escape(obj);
7230 // }
7231 // obj.field = 3;
7232 // while (!test()) {
7233 // if (test2()) { } else { obj.field = 5; }
7234 // }
7235 // EXIT
7236 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialLoopPhis4)7237 TEST_F(LoadStoreEliminationTest, PartialLoopPhis4) {
7238 ScopedObjectAccess soa(Thread::Current());
7239 VariableSizedHandleScope vshs(soa.Self());
7240 CreateGraph(/*handles=*/&vshs);
7241 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7242 "exit",
7243 {{"entry", "escape_check"},
7244 {"escape_check", "escape"},
7245 {"escape_check", "no_escape"},
7246 {"no_escape", "loop_pre_header"},
7247 {"escape", "loop_pre_header"},
7248
7249 {"loop_pre_header", "loop_header"},
7250 {"loop_header", "breturn"},
7251 {"loop_header", "loop_body"},
7252 {"loop_body", "loop_if_left"},
7253 {"loop_body", "loop_if_right"},
7254 {"loop_if_left", "loop_merge"},
7255 {"loop_if_right", "loop_merge"},
7256 {"loop_merge", "loop_header"},
7257
7258 {"breturn", "exit"}}));
7259 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7260 GET_BLOCK(entry);
7261 GET_BLOCK(exit);
7262 GET_BLOCK(breturn);
7263 GET_BLOCK(no_escape);
7264 GET_BLOCK(escape);
7265 GET_BLOCK(escape_check);
7266
7267 GET_BLOCK(loop_pre_header);
7268 GET_BLOCK(loop_header);
7269 GET_BLOCK(loop_body);
7270 GET_BLOCK(loop_if_left);
7271 GET_BLOCK(loop_if_right);
7272 GET_BLOCK(loop_merge);
7273 #undef GET_BLOCK
7274 EnsurePredecessorOrder(loop_pre_header, {no_escape, escape});
7275 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge});
7276 EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right});
7277 CHECK_SUBROUTINE_FAILURE();
7278 HInstruction* bool_val = MakeParam(DataType::Type::kBool);
7279 HInstruction* c3 = graph_->GetIntConstant(3);
7280 HInstruction* c5 = graph_->GetIntConstant(5);
7281
7282 HInstruction* cls = MakeClassLoad();
7283 HInstruction* new_inst = MakeNewInstance(cls);
7284 HInstruction* entry_goto = new (GetAllocator()) HGoto();
7285 entry->AddInstruction(cls);
7286 entry->AddInstruction(new_inst);
7287 entry->AddInstruction(entry_goto);
7288 ManuallyBuildEnvFor(cls, {});
7289 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7290
7291 HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val);
7292 escape_check->AddInstruction(if_esc_check);
7293
7294 HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst });
7295 HInstruction* goto_escape = new (GetAllocator()) HGoto();
7296 escape->AddInstruction(call_escape);
7297 escape->AddInstruction(goto_escape);
7298 call_escape->CopyEnvironmentFrom(cls->GetEnvironment());
7299
7300 HInstruction* goto_no_escape = new (GetAllocator()) HGoto();
7301 no_escape->AddInstruction(goto_no_escape);
7302
7303 HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7304 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
7305 loop_pre_header->AddInstruction(write_pre_header);
7306 loop_pre_header->AddInstruction(goto_preheader);
7307
7308 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
7309 HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {});
7310 HInstruction* if_header = new (GetAllocator()) HIf(call_header);
7311 loop_header->AddInstruction(suspend_check_header);
7312 loop_header->AddInstruction(call_header);
7313 loop_header->AddInstruction(if_header);
7314 call_header->CopyEnvironmentFrom(cls->GetEnvironment());
7315 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
7316
7317 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
7318 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
7319 loop_body->AddInstruction(call_loop_body);
7320 loop_body->AddInstruction(if_loop_body);
7321 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
7322
7323 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
7324 loop_if_left->AddInstruction(goto_loop_left);
7325
7326 HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32));
7327 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
7328 loop_if_right->AddInstruction(write_loop_right);
7329 loop_if_right->AddInstruction(goto_loop_right);
7330
7331 HInstruction* goto_loop_merge = new (GetAllocator()) HGoto();
7332 loop_merge->AddInstruction(goto_loop_merge);
7333
7334 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7335 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
7336 breturn->AddInstruction(read_bottom);
7337 breturn->AddInstruction(return_exit);
7338
7339 SetupExit(exit);
7340
7341 // PerformLSE expects this to be empty.
7342 graph_->ClearDominanceInformation();
7343 LOG(INFO) << "Pre LSE " << blks;
7344 PerformLSEWithPartial();
7345 LOG(INFO) << "Post LSE " << blks;
7346
7347 HPredicatedInstanceFieldGet* pred_get =
7348 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7349 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
7350 ASSERT_TRUE(pred_get != nullptr);
7351 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
7352 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
7353 EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant());
7354 EXPECT_INS_EQ(inst_return_phi->InputAt(1),
7355 FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor()));
7356 HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi();
7357 ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs();
7358 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
7359 HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge);
7360 EXPECT_INS_EQ(inst_value_phi, loop_header_phi);
7361 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
7362 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi);
7363 EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi);
7364 EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5);
7365 EXPECT_INS_RETAINED(write_loop_right) << *write_loop_right;
7366 EXPECT_TRUE(write_loop_right->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_loop_right;
7367 EXPECT_INS_RETAINED(write_pre_header) << *write_pre_header;
7368 EXPECT_TRUE(write_pre_header->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_pre_header;
7369 }
7370
7371 // // ENTRY
7372 // obj = new Obj();
7373 // obj.field = 3;
7374 // while (!test()) {
7375 // if (test2()) { } else { obj.field += 5; }
7376 // }
7377 // if (parameter_value) {
7378 // escape(obj);
7379 // }
7380 // EXIT
7381 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialLoopPhis5)7382 TEST_F(LoadStoreEliminationTest, PartialLoopPhis5) {
7383 ScopedObjectAccess soa(Thread::Current());
7384 VariableSizedHandleScope vshs(soa.Self());
7385 CreateGraph(/*handles=*/&vshs);
7386 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7387 "exit",
7388 {{"entry", "loop_pre_header"},
7389 {"loop_pre_header", "loop_header"},
7390 {"loop_header", "escape_check"},
7391 {"loop_header", "loop_body"},
7392 {"loop_body", "loop_if_left"},
7393 {"loop_body", "loop_if_right"},
7394 {"loop_if_left", "loop_merge"},
7395 {"loop_if_right", "loop_merge"},
7396 {"loop_merge", "loop_header"},
7397 {"escape_check", "escape"},
7398 {"escape_check", "no_escape"},
7399 {"no_escape", "breturn"},
7400 {"escape", "breturn"},
7401 {"breturn", "exit"}}));
7402 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7403 GET_BLOCK(entry);
7404 GET_BLOCK(exit);
7405 GET_BLOCK(breturn);
7406 GET_BLOCK(no_escape);
7407 GET_BLOCK(escape);
7408 GET_BLOCK(escape_check);
7409
7410 GET_BLOCK(loop_pre_header);
7411 GET_BLOCK(loop_header);
7412 GET_BLOCK(loop_body);
7413 GET_BLOCK(loop_if_left);
7414 GET_BLOCK(loop_if_right);
7415 GET_BLOCK(loop_merge);
7416 #undef GET_BLOCK
7417 EnsurePredecessorOrder(breturn, {no_escape, escape});
7418 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge});
7419 EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right});
7420 CHECK_SUBROUTINE_FAILURE();
7421 HInstruction* bool_val = MakeParam(DataType::Type::kBool);
7422 HInstruction* c3 = graph_->GetIntConstant(3);
7423 HInstruction* c5 = graph_->GetIntConstant(5);
7424
7425 HInstruction* cls = MakeClassLoad();
7426 HInstruction* new_inst = MakeNewInstance(cls);
7427 HInstruction* entry_goto = new (GetAllocator()) HGoto();
7428 entry->AddInstruction(cls);
7429 entry->AddInstruction(new_inst);
7430 entry->AddInstruction(entry_goto);
7431 ManuallyBuildEnvFor(cls, {});
7432 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7433
7434 HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7435 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
7436 loop_pre_header->AddInstruction(write_pre_header);
7437 loop_pre_header->AddInstruction(goto_preheader);
7438
7439 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
7440 HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {});
7441 HInstruction* if_header = new (GetAllocator()) HIf(call_header);
7442 loop_header->AddInstruction(suspend_check_header);
7443 loop_header->AddInstruction(call_header);
7444 loop_header->AddInstruction(if_header);
7445 call_header->CopyEnvironmentFrom(cls->GetEnvironment());
7446 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
7447
7448 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
7449 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
7450 loop_body->AddInstruction(call_loop_body);
7451 loop_body->AddInstruction(if_loop_body);
7452 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
7453
7454 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
7455 loop_if_left->AddInstruction(goto_loop_left);
7456
7457 HInstruction* read_loop_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7458 HInstruction* add_loop_right =
7459 new (GetAllocator()) HAdd(DataType::Type::kInt32, read_loop_right, c5);
7460 HInstruction* write_loop_right = MakeIFieldSet(new_inst, add_loop_right, MemberOffset(32));
7461 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
7462 loop_if_right->AddInstruction(read_loop_right);
7463 loop_if_right->AddInstruction(add_loop_right);
7464 loop_if_right->AddInstruction(write_loop_right);
7465 loop_if_right->AddInstruction(goto_loop_right);
7466
7467 HInstruction* goto_loop_merge = new (GetAllocator()) HGoto();
7468 loop_merge->AddInstruction(goto_loop_merge);
7469
7470 HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val);
7471 escape_check->AddInstruction(if_esc_check);
7472
7473 HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst });
7474 HInstruction* goto_escape = new (GetAllocator()) HGoto();
7475 escape->AddInstruction(call_escape);
7476 escape->AddInstruction(goto_escape);
7477 call_escape->CopyEnvironmentFrom(cls->GetEnvironment());
7478
7479 HInstruction* goto_no_escape = new (GetAllocator()) HGoto();
7480 no_escape->AddInstruction(goto_no_escape);
7481
7482 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7483 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
7484 breturn->AddInstruction(read_bottom);
7485 breturn->AddInstruction(return_exit);
7486
7487 SetupExit(exit);
7488
7489 // PerformLSE expects this to be empty.
7490 graph_->ClearDominanceInformation();
7491 LOG(INFO) << "Pre LSE " << blks;
7492 PerformLSEWithPartial();
7493 LOG(INFO) << "Post LSE " << blks;
7494
7495 HPredicatedInstanceFieldGet* pred_get =
7496 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7497 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
7498 ASSERT_TRUE(pred_get != nullptr);
7499 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
7500 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
7501 EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant());
7502 EXPECT_INS_EQ(inst_return_phi->InputAt(1),
7503 FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor()));
7504 HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi();
7505 ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs();
7506 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
7507 HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge);
7508 EXPECT_INS_EQ(inst_value_phi->InputAt(0), loop_header_phi);
7509 EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0));
7510 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
7511 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi);
7512 EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi);
7513 EXPECT_INS_EQ(loop_merge_phi->InputAt(1), add_loop_right);
7514 EXPECT_INS_EQ(add_loop_right->InputAt(0), loop_header_phi);
7515 EXPECT_INS_EQ(add_loop_right->InputAt(1), c5);
7516 HInstanceFieldSet* mat_set =
7517 FindSingleInstruction<HInstanceFieldSet>(graph_, escape->GetSinglePredecessor());
7518 ASSERT_NE(mat_set, nullptr);
7519 EXPECT_INS_EQ(mat_set->InputAt(1), loop_header_phi);
7520 EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right;
7521 EXPECT_INS_REMOVED(write_pre_header) << *write_pre_header;
7522 }
7523
7524 // // ENTRY
7525 // obj = new Obj();
7526 // obj.field = 3;
7527 // if (param) {
7528 // while (!test()) {
7529 // if (test2()) {
7530 // noescape();
7531 // } else {
7532 // abc = obj.field;
7533 // obj.field = abc + 5;
7534 // noescape();
7535 // }
7536 // }
7537 // escape(obj);
7538 // } else {
7539 // }
7540 // return obj.field
TEST_F(LoadStoreEliminationTest,PartialLoopPhis6)7541 TEST_F(LoadStoreEliminationTest, PartialLoopPhis6) {
7542 ScopedObjectAccess soa(Thread::Current());
7543 VariableSizedHandleScope vshs(soa.Self());
7544 CreateGraph(/*handles=*/&vshs);
7545 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7546 "exit",
7547 {{"entry", "start"},
7548 {"start", "left"},
7549 {"start", "right"},
7550 {"left", "loop_pre_header"},
7551
7552 {"loop_pre_header", "loop_header"},
7553 {"loop_header", "escape"},
7554 {"loop_header", "loop_body"},
7555 {"loop_body", "loop_if_left"},
7556 {"loop_body", "loop_if_right"},
7557 {"loop_if_left", "loop_header"},
7558 {"loop_if_right", "loop_header"},
7559
7560 {"escape", "breturn"},
7561 {"right", "breturn"},
7562 {"breturn", "exit"}}));
7563 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7564 GET_BLOCK(entry);
7565 GET_BLOCK(exit);
7566 GET_BLOCK(breturn);
7567 GET_BLOCK(left);
7568 GET_BLOCK(right);
7569 GET_BLOCK(start);
7570 GET_BLOCK(escape);
7571
7572 GET_BLOCK(loop_pre_header);
7573 GET_BLOCK(loop_header);
7574 GET_BLOCK(loop_body);
7575 GET_BLOCK(loop_if_left);
7576 GET_BLOCK(loop_if_right);
7577 #undef GET_BLOCK
7578 EnsurePredecessorOrder(breturn, {escape, right});
7579 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_if_left, loop_if_right});
7580 CHECK_SUBROUTINE_FAILURE();
7581 HInstruction* bool_val = MakeParam(DataType::Type::kBool);
7582 HInstruction* c3 = graph_->GetIntConstant(3);
7583 HInstruction* c5 = graph_->GetIntConstant(5);
7584
7585 HInstruction* cls = MakeClassLoad();
7586 HInstruction* new_inst = MakeNewInstance(cls);
7587 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7588 HInstruction* entry_goto = new (GetAllocator()) HGoto();
7589 entry->AddInstruction(cls);
7590 entry->AddInstruction(new_inst);
7591 entry->AddInstruction(write_entry);
7592 entry->AddInstruction(entry_goto);
7593 ManuallyBuildEnvFor(cls, {});
7594 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7595
7596 start->AddInstruction(new (GetAllocator()) HIf(bool_val));
7597
7598 HInstruction* left_goto = new (GetAllocator()) HGoto();
7599 left->AddInstruction(left_goto);
7600
7601 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
7602 loop_pre_header->AddInstruction(goto_preheader);
7603
7604 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
7605 HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {});
7606 HInstruction* if_header = new (GetAllocator()) HIf(call_header);
7607 loop_header->AddInstruction(suspend_check_header);
7608 loop_header->AddInstruction(call_header);
7609 loop_header->AddInstruction(if_header);
7610 call_header->CopyEnvironmentFrom(cls->GetEnvironment());
7611 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
7612
7613 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
7614 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
7615 loop_body->AddInstruction(call_loop_body);
7616 loop_body->AddInstruction(if_loop_body);
7617 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
7618
7619 HInstruction* call_loop_left = MakeInvoke(DataType::Type::kVoid, {});
7620 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
7621 loop_if_left->AddInstruction(call_loop_left);
7622 loop_if_left->AddInstruction(goto_loop_left);
7623 call_loop_left->CopyEnvironmentFrom(cls->GetEnvironment());
7624
7625 HInstruction* read_loop_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7626 HInstruction* add_loop_right =
7627 new (GetAllocator()) HAdd(DataType::Type::kInt32, c5, read_loop_right);
7628 HInstruction* write_loop_right = MakeIFieldSet(new_inst, add_loop_right, MemberOffset(32));
7629 HInstruction* call_loop_right = MakeInvoke(DataType::Type::kVoid, {});
7630 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
7631 loop_if_right->AddInstruction(read_loop_right);
7632 loop_if_right->AddInstruction(add_loop_right);
7633 loop_if_right->AddInstruction(write_loop_right);
7634 loop_if_right->AddInstruction(call_loop_right);
7635 loop_if_right->AddInstruction(goto_loop_right);
7636 call_loop_right->CopyEnvironmentFrom(cls->GetEnvironment());
7637
7638 HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst });
7639 HInstruction* goto_escape = new (GetAllocator()) HGoto();
7640 escape->AddInstruction(call_escape);
7641 escape->AddInstruction(goto_escape);
7642 call_escape->CopyEnvironmentFrom(cls->GetEnvironment());
7643
7644 HInstruction* goto_right = new (GetAllocator()) HGoto();
7645 right->AddInstruction(goto_right);
7646
7647 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7648 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
7649 breturn->AddInstruction(read_bottom);
7650 breturn->AddInstruction(return_exit);
7651
7652 SetupExit(exit);
7653
7654 // PerformLSE expects this to be empty.
7655 graph_->ClearDominanceInformation();
7656 LOG(INFO) << "Pre LSE " << blks;
7657 PerformLSEWithPartial();
7658 LOG(INFO) << "Post LSE " << blks;
7659
7660 HPredicatedInstanceFieldGet* pred_get =
7661 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7662 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
7663 ASSERT_TRUE(pred_get != nullptr);
7664 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
7665 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
7666 EXPECT_INS_EQ(inst_return_phi->InputAt(0),
7667 FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor()));
7668 EXPECT_INS_EQ(inst_return_phi->InputAt(1), graph_->GetNullConstant());
7669 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), graph_->GetIntConstant(0));
7670 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), c3);
7671 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
7672 ASSERT_NE(loop_header_phi, nullptr);
7673 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
7674 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_header_phi);
7675 EXPECT_INS_EQ(loop_header_phi->InputAt(2), add_loop_right);
7676 EXPECT_INS_EQ(add_loop_right->InputAt(0), c5);
7677 EXPECT_INS_EQ(add_loop_right->InputAt(1), loop_header_phi);
7678 HInstanceFieldSet* mat_set =
7679 FindSingleInstruction<HInstanceFieldSet>(graph_, escape->GetSinglePredecessor());
7680 ASSERT_NE(mat_set, nullptr);
7681 EXPECT_INS_EQ(mat_set->InputAt(1), loop_header_phi);
7682 EXPECT_INS_REMOVED(write_loop_right);
7683 EXPECT_INS_REMOVED(write_entry);
7684 EXPECT_INS_RETAINED(call_header);
7685 EXPECT_INS_RETAINED(call_loop_left);
7686 EXPECT_INS_RETAINED(call_loop_right);
7687 }
7688
7689 // TODO This should really be in an Instruction simplifier Gtest but (1) that
7690 // doesn't exist and (2) we should move this simplification to directly in the
7691 // LSE pass since there is more information then.
7692 // // ENTRY
7693 // obj = new Obj();
7694 // obj.field = 3;
7695 // if (param) {
7696 // escape(obj);
7697 // } else {
7698 // obj.field = 10;
7699 // }
7700 // return obj.field;
TEST_F(LoadStoreEliminationTest,SimplifyTest)7701 TEST_F(LoadStoreEliminationTest, SimplifyTest) {
7702 ScopedObjectAccess soa(Thread::Current());
7703 VariableSizedHandleScope vshs(soa.Self());
7704 CreateGraph(&vshs);
7705 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7706 "exit",
7707 {{"entry", "left"},
7708 {"entry", "right"},
7709 {"left", "breturn"},
7710 {"right", "breturn"},
7711 {"breturn", "exit"}}));
7712 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7713 GET_BLOCK(entry);
7714 GET_BLOCK(exit);
7715 GET_BLOCK(breturn);
7716 GET_BLOCK(left);
7717 GET_BLOCK(right);
7718 #undef GET_BLOCK
7719 EnsurePredecessorOrder(breturn, {left, right});
7720
7721 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
7722 HInstruction* c3 = graph_->GetIntConstant(3);
7723 HInstruction* c10 = graph_->GetIntConstant(10);
7724
7725 HInstruction* cls = MakeClassLoad();
7726 HInstruction* new_inst = MakeNewInstance(cls);
7727 HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7728 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
7729 entry->AddInstruction(cls);
7730 entry->AddInstruction(new_inst);
7731 entry->AddInstruction(write_start);
7732 entry->AddInstruction(if_inst);
7733 ManuallyBuildEnvFor(cls, {});
7734 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7735
7736 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
7737 HInstruction* goto_left = new (GetAllocator()) HGoto();
7738 left->AddInstruction(call_left);
7739 left->AddInstruction(goto_left);
7740 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
7741
7742 HInstruction* write_right = MakeIFieldSet(new_inst, c10, MemberOffset(32));
7743 HInstruction* goto_right = new (GetAllocator()) HGoto();
7744 right->AddInstruction(write_right);
7745 right->AddInstruction(goto_right);
7746
7747 HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7748 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
7749 breturn->AddInstruction(read_end);
7750 breturn->AddInstruction(return_exit);
7751
7752 SetupExit(exit);
7753
7754 // PerformLSE expects this to be empty.
7755 graph_->ClearDominanceInformation();
7756 LOG(INFO) << "Pre LSE " << blks;
7757 PerformLSE();
7758
7759 // Run the code-simplifier too
7760 LOG(INFO) << "Pre simplification " << blks;
7761 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
7762 simp.Run();
7763
7764 LOG(INFO) << "Post LSE " << blks;
7765
7766 EXPECT_INS_REMOVED(write_right);
7767 EXPECT_INS_REMOVED(write_start);
7768 EXPECT_INS_REMOVED(read_end);
7769 EXPECT_INS_RETAINED(call_left);
7770
7771 HPredicatedInstanceFieldGet* pred_get =
7772 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7773 ASSERT_NE(pred_get, nullptr);
7774 EXPECT_INS_EQ(pred_get->GetDefaultValue(), c10);
7775 }
7776
7777
7778 // TODO This should really be in an Instruction simplifier Gtest but (1) that
7779 // doesn't exist and (2) we should move this simplification to directly in the
7780 // LSE pass since there is more information then.
7781 //
7782 // This checks that we don't replace phis when the replacement isn't valid at
7783 // that point (i.e. it doesn't dominate)
7784 // // ENTRY
7785 // obj = new Obj();
7786 // obj.field = 3;
7787 // if (param) {
7788 // escape(obj);
7789 // } else {
7790 // obj.field = noescape();
7791 // }
7792 // return obj.field;
TEST_F(LoadStoreEliminationTest,SimplifyTest2)7793 TEST_F(LoadStoreEliminationTest, SimplifyTest2) {
7794 ScopedObjectAccess soa(Thread::Current());
7795 VariableSizedHandleScope vshs(soa.Self());
7796 CreateGraph(&vshs);
7797 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7798 "exit",
7799 {{"entry", "left"},
7800 {"entry", "right"},
7801 {"left", "breturn"},
7802 {"right", "breturn"},
7803 {"breturn", "exit"}}));
7804 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7805 GET_BLOCK(entry);
7806 GET_BLOCK(exit);
7807 GET_BLOCK(breturn);
7808 GET_BLOCK(left);
7809 GET_BLOCK(right);
7810 #undef GET_BLOCK
7811 EnsurePredecessorOrder(breturn, {left, right});
7812
7813 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
7814 HInstruction* c3 = graph_->GetIntConstant(3);
7815
7816 HInstruction* cls = MakeClassLoad();
7817 HInstruction* new_inst = MakeNewInstance(cls);
7818 HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7819 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
7820 entry->AddInstruction(cls);
7821 entry->AddInstruction(new_inst);
7822 entry->AddInstruction(write_start);
7823 entry->AddInstruction(if_inst);
7824 ManuallyBuildEnvFor(cls, {});
7825 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7826
7827 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, {new_inst});
7828 HInstruction* goto_left = new (GetAllocator()) HGoto();
7829 left->AddInstruction(call_left);
7830 left->AddInstruction(goto_left);
7831 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
7832
7833 HInstruction* call_right = MakeInvoke(DataType::Type::kInt32, {});
7834 HInstruction* write_right = MakeIFieldSet(new_inst, call_right, MemberOffset(32));
7835 HInstruction* goto_right = new (GetAllocator()) HGoto();
7836 right->AddInstruction(call_right);
7837 right->AddInstruction(write_right);
7838 right->AddInstruction(goto_right);
7839 call_right->CopyEnvironmentFrom(cls->GetEnvironment());
7840
7841 HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7842 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
7843 breturn->AddInstruction(read_end);
7844 breturn->AddInstruction(return_exit);
7845
7846 SetupExit(exit);
7847
7848 // PerformLSE expects this to be empty.
7849 graph_->ClearDominanceInformation();
7850 LOG(INFO) << "Pre LSE " << blks;
7851 PerformLSE();
7852
7853 // Run the code-simplifier too
7854 LOG(INFO) << "Pre simplification " << blks;
7855 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
7856 simp.Run();
7857
7858 LOG(INFO) << "Post LSE " << blks;
7859
7860 EXPECT_INS_REMOVED(write_right);
7861 EXPECT_INS_REMOVED(write_start);
7862 EXPECT_INS_REMOVED(read_end);
7863 EXPECT_INS_RETAINED(call_left);
7864 EXPECT_INS_RETAINED(call_right);
7865 EXPECT_EQ(call_right->GetBlock(), right);
7866
7867 HPredicatedInstanceFieldGet* pred_get =
7868 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7869 ASSERT_NE(pred_get, nullptr);
7870 EXPECT_TRUE(pred_get->GetDefaultValue()->IsPhi()) << pred_get->DumpWithArgs();
7871 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), graph_->GetIntConstant(0))
7872 << pred_get->DumpWithArgs();
7873 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), call_right) << pred_get->DumpWithArgs();
7874 }
7875
7876 // TODO This should really be in an Instruction simplifier Gtest but (1) that
7877 // doesn't exist and (2) we should move this simplification to directly in the
7878 // LSE pass since there is more information then.
7879 //
7880 // This checks that we replace phis even when there are multiple replacements as
7881 // long as they are equal
7882 // // ENTRY
7883 // obj = new Obj();
7884 // obj.field = 3;
7885 // switch (param) {
7886 // case 1:
7887 // escape(obj);
7888 // break;
7889 // case 2:
7890 // obj.field = 10;
7891 // break;
7892 // case 3:
7893 // obj.field = 10;
7894 // break;
7895 // }
7896 // return obj.field;
TEST_F(LoadStoreEliminationTest,SimplifyTest3)7897 TEST_F(LoadStoreEliminationTest, SimplifyTest3) {
7898 ScopedObjectAccess soa(Thread::Current());
7899 VariableSizedHandleScope vshs(soa.Self());
7900 CreateGraph(&vshs);
7901 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7902 "exit",
7903 {{"entry", "case1"},
7904 {"entry", "case2"},
7905 {"entry", "case3"},
7906 {"case1", "breturn"},
7907 {"case2", "breturn"},
7908 {"case3", "breturn"},
7909 {"breturn", "exit"}}));
7910 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7911 GET_BLOCK(entry);
7912 GET_BLOCK(exit);
7913 GET_BLOCK(breturn);
7914 GET_BLOCK(case1);
7915 GET_BLOCK(case2);
7916 GET_BLOCK(case3);
7917 #undef GET_BLOCK
7918 EnsurePredecessorOrder(breturn, {case1, case2, case3});
7919
7920 HInstruction* int_val = MakeParam(DataType::Type::kInt32);
7921 HInstruction* c3 = graph_->GetIntConstant(3);
7922 HInstruction* c10 = graph_->GetIntConstant(10);
7923
7924 HInstruction* cls = MakeClassLoad();
7925 HInstruction* new_inst = MakeNewInstance(cls);
7926 HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7927 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val);
7928 entry->AddInstruction(cls);
7929 entry->AddInstruction(new_inst);
7930 entry->AddInstruction(write_start);
7931 entry->AddInstruction(switch_inst);
7932 ManuallyBuildEnvFor(cls, {});
7933 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7934
7935 HInstruction* call_case1 = MakeInvoke(DataType::Type::kVoid, {new_inst});
7936 HInstruction* goto_case1 = new (GetAllocator()) HGoto();
7937 case1->AddInstruction(call_case1);
7938 case1->AddInstruction(goto_case1);
7939 call_case1->CopyEnvironmentFrom(cls->GetEnvironment());
7940
7941 HInstruction* write_case2 = MakeIFieldSet(new_inst, c10, MemberOffset(32));
7942 HInstruction* goto_case2 = new (GetAllocator()) HGoto();
7943 case2->AddInstruction(write_case2);
7944 case2->AddInstruction(goto_case2);
7945
7946 HInstruction* write_case3 = MakeIFieldSet(new_inst, c10, MemberOffset(32));
7947 HInstruction* goto_case3 = new (GetAllocator()) HGoto();
7948 case3->AddInstruction(write_case3);
7949 case3->AddInstruction(goto_case3);
7950
7951 HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7952 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
7953 breturn->AddInstruction(read_end);
7954 breturn->AddInstruction(return_exit);
7955
7956 SetupExit(exit);
7957
7958 // PerformLSE expects this to be empty.
7959 graph_->ClearDominanceInformation();
7960 LOG(INFO) << "Pre LSE " << blks;
7961 PerformLSE();
7962
7963 // Run the code-simplifier too
7964 LOG(INFO) << "Pre simplification " << blks;
7965 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
7966 simp.Run();
7967
7968 LOG(INFO) << "Post LSE " << blks;
7969
7970 EXPECT_INS_REMOVED(write_case2);
7971 EXPECT_INS_REMOVED(write_case3);
7972 EXPECT_INS_REMOVED(write_start);
7973 EXPECT_INS_REMOVED(read_end);
7974 EXPECT_INS_RETAINED(call_case1);
7975
7976 HPredicatedInstanceFieldGet* pred_get =
7977 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7978 ASSERT_NE(pred_get, nullptr);
7979 EXPECT_INS_EQ(pred_get->GetDefaultValue(), c10)
7980 << pred_get->DumpWithArgs();
7981 }
7982
7983 // TODO This should really be in an Instruction simplifier Gtest but (1) that
7984 // doesn't exist and (2) we should move this simplification to directly in the
7985 // LSE pass since there is more information then.
7986 //
7987 // This checks that we don't replace phis even when there are multiple
7988 // replacements if they are not equal
7989 // // ENTRY
7990 // obj = new Obj();
7991 // obj.field = 3;
7992 // switch (param) {
7993 // case 1:
7994 // escape(obj);
7995 // break;
7996 // case 2:
7997 // obj.field = 10;
7998 // break;
7999 // case 3:
8000 // obj.field = 20;
8001 // break;
8002 // }
8003 // return obj.field;
TEST_F(LoadStoreEliminationTest,SimplifyTest4)8004 TEST_F(LoadStoreEliminationTest, SimplifyTest4) {
8005 ScopedObjectAccess soa(Thread::Current());
8006 VariableSizedHandleScope vshs(soa.Self());
8007 CreateGraph(&vshs);
8008 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
8009 "exit",
8010 {{"entry", "case1"},
8011 {"entry", "case2"},
8012 {"entry", "case3"},
8013 {"case1", "breturn"},
8014 {"case2", "breturn"},
8015 {"case3", "breturn"},
8016 {"breturn", "exit"}}));
8017 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
8018 GET_BLOCK(entry);
8019 GET_BLOCK(exit);
8020 GET_BLOCK(breturn);
8021 GET_BLOCK(case1);
8022 GET_BLOCK(case2);
8023 GET_BLOCK(case3);
8024 #undef GET_BLOCK
8025 EnsurePredecessorOrder(breturn, {case1, case2, case3});
8026
8027 HInstruction* int_val = MakeParam(DataType::Type::kInt32);
8028 HInstruction* c3 = graph_->GetIntConstant(3);
8029 HInstruction* c10 = graph_->GetIntConstant(10);
8030 HInstruction* c20 = graph_->GetIntConstant(20);
8031
8032 HInstruction* cls = MakeClassLoad();
8033 HInstruction* new_inst = MakeNewInstance(cls);
8034 HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32));
8035 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val);
8036 entry->AddInstruction(cls);
8037 entry->AddInstruction(new_inst);
8038 entry->AddInstruction(write_start);
8039 entry->AddInstruction(switch_inst);
8040 ManuallyBuildEnvFor(cls, {});
8041 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
8042
8043 HInstruction* call_case1 = MakeInvoke(DataType::Type::kVoid, {new_inst});
8044 HInstruction* goto_case1 = new (GetAllocator()) HGoto();
8045 case1->AddInstruction(call_case1);
8046 case1->AddInstruction(goto_case1);
8047 call_case1->CopyEnvironmentFrom(cls->GetEnvironment());
8048
8049 HInstruction* write_case2 = MakeIFieldSet(new_inst, c10, MemberOffset(32));
8050 HInstruction* goto_case2 = new (GetAllocator()) HGoto();
8051 case2->AddInstruction(write_case2);
8052 case2->AddInstruction(goto_case2);
8053
8054 HInstruction* write_case3 = MakeIFieldSet(new_inst, c20, MemberOffset(32));
8055 HInstruction* goto_case3 = new (GetAllocator()) HGoto();
8056 case3->AddInstruction(write_case3);
8057 case3->AddInstruction(goto_case3);
8058
8059 HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
8060 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
8061 breturn->AddInstruction(read_end);
8062 breturn->AddInstruction(return_exit);
8063
8064 SetupExit(exit);
8065
8066 // PerformLSE expects this to be empty.
8067 graph_->ClearDominanceInformation();
8068 LOG(INFO) << "Pre LSE " << blks;
8069 PerformLSE();
8070
8071 // Run the code-simplifier too
8072 LOG(INFO) << "Pre simplification " << blks;
8073 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
8074 simp.Run();
8075
8076 LOG(INFO) << "Post LSE " << blks;
8077
8078 EXPECT_INS_REMOVED(write_case2);
8079 EXPECT_INS_REMOVED(write_case3);
8080 EXPECT_INS_REMOVED(write_start);
8081 EXPECT_INS_REMOVED(read_end);
8082 EXPECT_INS_RETAINED(call_case1);
8083
8084 HPredicatedInstanceFieldGet* pred_get =
8085 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
8086 ASSERT_NE(pred_get, nullptr);
8087 EXPECT_TRUE(pred_get->GetDefaultValue()->IsPhi())
8088 << pred_get->DumpWithArgs();
8089 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), graph_->GetIntConstant(0));
8090 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), c10);
8091 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(2), c20);
8092 }
8093
8094 // Make sure that irreducible loops don't screw up Partial LSE. We can't pull
8095 // phis through them so we need to treat them as escapes.
8096 // TODO We should be able to do better than this? Need to do some research.
8097 // // ENTRY
8098 // obj = new Obj();
8099 // obj.foo = 11;
8100 // if (param1) {
8101 // } else {
8102 // // irreducible loop here. NB the objdoesn't actually escape
8103 // obj.foo = 33;
8104 // if (param2) {
8105 // goto inner;
8106 // } else {
8107 // while (test()) {
8108 // if (test()) {
8109 // obj.foo = 66;
8110 // } else {
8111 // }
8112 // inner:
8113 // }
8114 // }
8115 // }
8116 // return obj.foo;
TEST_F(LoadStoreEliminationTest,PartialIrreducibleLoop)8117 TEST_F(LoadStoreEliminationTest, PartialIrreducibleLoop) {
8118 ScopedObjectAccess soa(Thread::Current());
8119 VariableSizedHandleScope vshs(soa.Self());
8120 CreateGraph(&vshs);
8121 AdjacencyListGraph blks(SetupFromAdjacencyList("start",
8122 "exit",
8123 {{"start", "entry"},
8124 {"entry", "left"},
8125 {"entry", "right"},
8126 {"left", "breturn"},
8127
8128 {"right", "right_crit_break_loop"},
8129 {"right_crit_break_loop", "loop_header"},
8130 {"right", "right_crit_break_end"},
8131 {"right_crit_break_end", "loop_end"},
8132
8133 {"loop_header", "loop_body"},
8134 {"loop_body", "loop_left"},
8135 {"loop_body", "loop_right"},
8136 {"loop_left", "loop_end"},
8137 {"loop_right", "loop_end"},
8138 {"loop_end", "loop_header"},
8139 {"loop_header", "loop_header_crit_break"},
8140 {"loop_header_crit_break", "breturn"},
8141
8142 {"breturn", "exit"}}));
8143 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
8144 GET_BLOCK(start);
8145 GET_BLOCK(entry);
8146 GET_BLOCK(exit);
8147 GET_BLOCK(breturn);
8148 GET_BLOCK(left);
8149 GET_BLOCK(right);
8150 GET_BLOCK(right_crit_break_end);
8151 GET_BLOCK(right_crit_break_loop);
8152 GET_BLOCK(loop_header);
8153 GET_BLOCK(loop_header_crit_break);
8154 GET_BLOCK(loop_body);
8155 GET_BLOCK(loop_left);
8156 GET_BLOCK(loop_right);
8157 GET_BLOCK(loop_end);
8158 #undef GET_BLOCK
8159 EnsurePredecessorOrder(breturn, {left, loop_header_crit_break});
8160 HInstruction* c11 = graph_->GetIntConstant(11);
8161 HInstruction* c33 = graph_->GetIntConstant(33);
8162 HInstruction* c66 = graph_->GetIntConstant(66);
8163 HInstruction* param1 = MakeParam(DataType::Type::kBool);
8164 HInstruction* param2 = MakeParam(DataType::Type::kBool);
8165
8166 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
8167 HInstruction* start_goto = new (GetAllocator()) HGoto();
8168 start->AddInstruction(suspend);
8169 start->AddInstruction(start_goto);
8170 ManuallyBuildEnvFor(suspend, {});
8171
8172 HInstruction* cls = MakeClassLoad();
8173 HInstruction* new_inst = MakeNewInstance(cls);
8174 HInstruction* write_start = MakeIFieldSet(new_inst, c11, MemberOffset(32));
8175 HInstruction* if_inst = new (GetAllocator()) HIf(param1);
8176 entry->AddInstruction(cls);
8177 entry->AddInstruction(new_inst);
8178 entry->AddInstruction(write_start);
8179 entry->AddInstruction(if_inst);
8180 ManuallyBuildEnvFor(cls, {});
8181 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
8182
8183 left->AddInstruction(new (GetAllocator()) HGoto());
8184
8185 right->AddInstruction(MakeIFieldSet(new_inst, c33, MemberOffset(32)));
8186 right->AddInstruction(new (GetAllocator()) HIf(param2));
8187
8188 right_crit_break_end->AddInstruction(new (GetAllocator()) HGoto());
8189 right_crit_break_loop->AddInstruction(new (GetAllocator()) HGoto());
8190
8191 HInstruction* header_suspend = new (GetAllocator()) HSuspendCheck();
8192 HInstruction* header_invoke = MakeInvoke(DataType::Type::kBool, {});
8193 HInstruction* header_if = new (GetAllocator()) HIf(header_invoke);
8194 loop_header->AddInstruction(header_suspend);
8195 loop_header->AddInstruction(header_invoke);
8196 loop_header->AddInstruction(header_if);
8197 header_suspend->CopyEnvironmentFrom(cls->GetEnvironment());
8198 header_invoke->CopyEnvironmentFrom(cls->GetEnvironment());
8199
8200 HInstruction* body_invoke = MakeInvoke(DataType::Type::kBool, {});
8201 HInstruction* body_if = new (GetAllocator()) HIf(body_invoke);
8202 loop_body->AddInstruction(body_invoke);
8203 loop_body->AddInstruction(body_if);
8204 body_invoke->CopyEnvironmentFrom(cls->GetEnvironment());
8205
8206 HInstruction* left_set = MakeIFieldSet(new_inst, c66, MemberOffset(32));
8207 HInstruction* left_goto = MakeIFieldSet(new_inst, c66, MemberOffset(32));
8208 loop_left->AddInstruction(left_set);
8209 loop_left->AddInstruction(left_goto);
8210
8211 loop_right->AddInstruction(new (GetAllocator()) HGoto());
8212
8213 loop_end->AddInstruction(new (GetAllocator()) HGoto());
8214
8215 HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
8216 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
8217 breturn->AddInstruction(read_end);
8218 breturn->AddInstruction(return_exit);
8219
8220 SetupExit(exit);
8221
8222 // PerformLSE expects this to be empty.
8223 graph_->ClearDominanceInformation();
8224 LOG(INFO) << "Pre LSE " << blks;
8225 PerformLSE();
8226 LOG(INFO) << "Post LSE " << blks;
8227
8228 EXPECT_TRUE(loop_header->IsLoopHeader());
8229 EXPECT_TRUE(loop_header->GetLoopInformation()->IsIrreducible());
8230
8231 EXPECT_INS_RETAINED(left_set);
8232 EXPECT_INS_REMOVED(write_start);
8233 EXPECT_INS_REMOVED(read_end);
8234
8235 HPredicatedInstanceFieldGet* pred_get =
8236 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
8237 ASSERT_NE(pred_get, nullptr);
8238 ASSERT_TRUE(pred_get->GetDefaultValue()->IsPhi()) << pred_get->DumpWithArgs();
8239 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), c11);
8240 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), graph_->GetIntConstant(0));
8241 ASSERT_TRUE(pred_get->GetTarget()->IsPhi()) << pred_get->DumpWithArgs();
8242 EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(0), graph_->GetNullConstant());
8243 HNewInstance* mat = FindSingleInstruction<HNewInstance>(graph_, right->GetSinglePredecessor());
8244 ASSERT_NE(mat, nullptr);
8245 EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(1), mat);
8246 }
8247
8248 enum class UsesOrder { kDefaultOrder, kReverseOrder };
operator <<(std::ostream & os,const UsesOrder & ord)8249 std::ostream& operator<<(std::ostream& os, const UsesOrder& ord) {
8250 switch (ord) {
8251 case UsesOrder::kDefaultOrder:
8252 return os << "DefaultOrder";
8253 case UsesOrder::kReverseOrder:
8254 return os << "ReverseOrder";
8255 }
8256 }
8257
8258 class UsesOrderDependentTestGroup
8259 : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<UsesOrder>> {};
8260
8261 // Make sure that we record replacements by predicated loads and use them
8262 // instead of constructing Phis with inputs removed from the graph. Bug: 183897743
8263 // Note that the bug was hit only for a certain ordering of the NewInstance
8264 // uses, so we test both orderings.
8265 // // ENTRY
8266 // obj = new Obj();
8267 // obj.foo = 11;
8268 // if (param1) {
8269 // // LEFT1
8270 // escape(obj);
8271 // } else {
8272 // // RIGHT1
8273 // }
8274 // // MIDDLE
8275 // a = obj.foo;
8276 // if (param2) {
8277 // // LEFT2
8278 // obj.foo = 33;
8279 // } else {
8280 // // RIGHT2
8281 // }
8282 // // BRETURN
8283 // no_escape() // If `obj` escaped, the field value can change. (Avoid non-partial LSE.)
8284 // b = obj.foo;
8285 // return a + b;
TEST_P(UsesOrderDependentTestGroup,RecordPredicatedReplacements1)8286 TEST_P(UsesOrderDependentTestGroup, RecordPredicatedReplacements1) {
8287 ScopedObjectAccess soa(Thread::Current());
8288 VariableSizedHandleScope vshs(soa.Self());
8289 CreateGraph(&vshs);
8290 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
8291 "exit",
8292 {{"entry", "left1"},
8293 {"entry", "right1"},
8294 {"left1", "middle"},
8295 {"right1", "middle"},
8296 {"middle", "left2"},
8297 {"middle", "right2"},
8298 {"left2", "breturn"},
8299 {"right2", "breturn"},
8300 {"breturn", "exit"}}));
8301 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
8302 GET_BLOCK(entry);
8303 GET_BLOCK(left1);
8304 GET_BLOCK(right1);
8305 GET_BLOCK(middle);
8306 GET_BLOCK(left2);
8307 GET_BLOCK(right2);
8308 GET_BLOCK(breturn);
8309 GET_BLOCK(exit);
8310 #undef GET_BLOCK
8311 EnsurePredecessorOrder(middle, {left1, right1});
8312 EnsurePredecessorOrder(breturn, {left2, right2});
8313 HInstruction* c0 = graph_->GetIntConstant(0);
8314 HInstruction* cnull = graph_->GetNullConstant();
8315 HInstruction* c11 = graph_->GetIntConstant(11);
8316 HInstruction* c33 = graph_->GetIntConstant(33);
8317 HInstruction* param1 = MakeParam(DataType::Type::kBool);
8318 HInstruction* param2 = MakeParam(DataType::Type::kBool);
8319
8320 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
8321 HInstruction* cls = MakeClassLoad();
8322 HInstruction* new_inst = MakeNewInstance(cls);
8323 HInstruction* entry_write = MakeIFieldSet(new_inst, c11, MemberOffset(32));
8324 HInstruction* entry_if = new (GetAllocator()) HIf(param1);
8325 entry->AddInstruction(suspend);
8326 entry->AddInstruction(cls);
8327 entry->AddInstruction(new_inst);
8328 entry->AddInstruction(entry_write);
8329 entry->AddInstruction(entry_if);
8330 ManuallyBuildEnvFor(suspend, {});
8331 ManuallyBuildEnvFor(cls, {});
8332 ManuallyBuildEnvFor(new_inst, {});
8333
8334 HInstruction* left1_call = MakeInvoke(DataType::Type::kVoid, { new_inst });
8335 HInstruction* left1_goto = new (GetAllocator()) HGoto();
8336 left1->AddInstruction(left1_call);
8337 left1->AddInstruction(left1_goto);
8338 ManuallyBuildEnvFor(left1_call, {});
8339
8340 HInstruction* right1_goto = new (GetAllocator()) HGoto();
8341 right1->AddInstruction(right1_goto);
8342
8343 HInstruction* middle_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
8344 HInstruction* middle_if = new (GetAllocator()) HIf(param2);
8345 if (GetParam() == UsesOrder::kDefaultOrder) {
8346 middle->AddInstruction(middle_read);
8347 }
8348 middle->AddInstruction(middle_if);
8349
8350 HInstanceFieldSet* left2_write = MakeIFieldSet(new_inst, c33, MemberOffset(32));
8351 HInstruction* left2_goto = new (GetAllocator()) HGoto();
8352 left2->AddInstruction(left2_write);
8353 left2->AddInstruction(left2_goto);
8354
8355 HInstruction* right2_goto = new (GetAllocator()) HGoto();
8356 right2->AddInstruction(right2_goto);
8357
8358 HInstruction* breturn_call = MakeInvoke(DataType::Type::kVoid, {});
8359 HInstruction* breturn_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
8360 HInstruction* breturn_add =
8361 new (GetAllocator()) HAdd(DataType::Type::kInt32, middle_read, breturn_read);
8362 HInstruction* breturn_return = new (GetAllocator()) HReturn(breturn_add);
8363 breturn->AddInstruction(breturn_call);
8364 breturn->AddInstruction(breturn_read);
8365 breturn->AddInstruction(breturn_add);
8366 breturn->AddInstruction(breturn_return);
8367 ManuallyBuildEnvFor(breturn_call, {});
8368
8369 if (GetParam() == UsesOrder::kReverseOrder) {
8370 // Insert `middle_read` in the same position as for the `kDefaultOrder` case.
8371 // The only difference is the order of entries in `new_inst->GetUses()` which
8372 // is used by `HeapReferenceData::CollectReplacements()` and defines the order
8373 // of instructions to process for `HeapReferenceData::PredicateInstructions()`.
8374 middle->InsertInstructionBefore(middle_read, middle_if);
8375 }
8376
8377 SetupExit(exit);
8378
8379 // PerformLSE expects this to be empty.
8380 graph_->ClearDominanceInformation();
8381 LOG(INFO) << "Pre LSE " << blks;
8382 PerformLSE();
8383 LOG(INFO) << "Post LSE " << blks;
8384
8385 EXPECT_INS_RETAINED(cls);
8386 EXPECT_INS_REMOVED(new_inst);
8387 HNewInstance* replacement_new_inst = FindSingleInstruction<HNewInstance>(graph_);
8388 ASSERT_NE(replacement_new_inst, nullptr);
8389 EXPECT_INS_REMOVED(entry_write);
8390 std::vector<HInstanceFieldSet*> all_writes;
8391 std::tie(all_writes) = FindAllInstructions<HInstanceFieldSet>(graph_);
8392 ASSERT_EQ(2u, all_writes.size());
8393 ASSERT_NE(all_writes[0] == left2_write, all_writes[1] == left2_write);
8394 HInstanceFieldSet* replacement_write = all_writes[(all_writes[0] == left2_write) ? 1u : 0u];
8395 ASSERT_FALSE(replacement_write->GetIsPredicatedSet());
8396 ASSERT_INS_EQ(replacement_write->InputAt(0), replacement_new_inst);
8397 ASSERT_INS_EQ(replacement_write->InputAt(1), c11);
8398
8399 EXPECT_INS_RETAINED(left1_call);
8400
8401 EXPECT_INS_REMOVED(middle_read);
8402 HPredicatedInstanceFieldGet* replacement_middle_read =
8403 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, middle);
8404 ASSERT_NE(replacement_middle_read, nullptr);
8405 ASSERT_TRUE(replacement_middle_read->GetTarget()->IsPhi());
8406 ASSERT_EQ(2u, replacement_middle_read->GetTarget()->AsPhi()->InputCount());
8407 ASSERT_INS_EQ(replacement_middle_read->GetTarget()->AsPhi()->InputAt(0), replacement_new_inst);
8408 ASSERT_INS_EQ(replacement_middle_read->GetTarget()->AsPhi()->InputAt(1), cnull);
8409 ASSERT_TRUE(replacement_middle_read->GetDefaultValue()->IsPhi());
8410 ASSERT_EQ(2u, replacement_middle_read->GetDefaultValue()->AsPhi()->InputCount());
8411 ASSERT_INS_EQ(replacement_middle_read->GetDefaultValue()->AsPhi()->InputAt(0), c0);
8412 ASSERT_INS_EQ(replacement_middle_read->GetDefaultValue()->AsPhi()->InputAt(1), c11);
8413
8414 EXPECT_INS_RETAINED(left2_write);
8415 ASSERT_TRUE(left2_write->GetIsPredicatedSet());
8416
8417 EXPECT_INS_REMOVED(breturn_read);
8418 HPredicatedInstanceFieldGet* replacement_breturn_read =
8419 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
8420 ASSERT_NE(replacement_breturn_read, nullptr);
8421 ASSERT_INS_EQ(replacement_breturn_read->GetTarget(), replacement_middle_read->GetTarget());
8422 ASSERT_TRUE(replacement_breturn_read->GetDefaultValue()->IsPhi());
8423 ASSERT_EQ(2u, replacement_breturn_read->GetDefaultValue()->AsPhi()->InputCount());
8424 ASSERT_INS_EQ(replacement_breturn_read->GetDefaultValue()->AsPhi()->InputAt(0), c33);
8425 HInstruction* other_input = replacement_breturn_read->GetDefaultValue()->AsPhi()->InputAt(1);
8426 ASSERT_NE(other_input->GetBlock(), nullptr) << GetParam();
8427 ASSERT_INS_EQ(other_input, replacement_middle_read);
8428 }
8429
8430 // Regression test for a bad DCHECK() found while trying to write a test for b/188188275.
8431 // // ENTRY
8432 // obj = new Obj();
8433 // obj.foo = 11;
8434 // if (param1) {
8435 // // LEFT1
8436 // escape(obj);
8437 // } else {
8438 // // RIGHT1
8439 // }
8440 // // MIDDLE
8441 // a = obj.foo;
8442 // if (param2) {
8443 // // LEFT2
8444 // no_escape();
8445 // } else {
8446 // // RIGHT2
8447 // }
8448 // // BRETURN
8449 // b = obj.foo;
8450 // return a + b;
TEST_P(UsesOrderDependentTestGroup,RecordPredicatedReplacements2)8451 TEST_P(UsesOrderDependentTestGroup, RecordPredicatedReplacements2) {
8452 ScopedObjectAccess soa(Thread::Current());
8453 VariableSizedHandleScope vshs(soa.Self());
8454 CreateGraph(&vshs);
8455 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
8456 "exit",
8457 {{"entry", "left1"},
8458 {"entry", "right1"},
8459 {"left1", "middle"},
8460 {"right1", "middle"},
8461 {"middle", "left2"},
8462 {"middle", "right2"},
8463 {"left2", "breturn"},
8464 {"right2", "breturn"},
8465 {"breturn", "exit"}}));
8466 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
8467 GET_BLOCK(entry);
8468 GET_BLOCK(left1);
8469 GET_BLOCK(right1);
8470 GET_BLOCK(middle);
8471 GET_BLOCK(left2);
8472 GET_BLOCK(right2);
8473 GET_BLOCK(breturn);
8474 GET_BLOCK(exit);
8475 #undef GET_BLOCK
8476 EnsurePredecessorOrder(middle, {left1, right1});
8477 EnsurePredecessorOrder(breturn, {left2, right2});
8478 HInstruction* c0 = graph_->GetIntConstant(0);
8479 HInstruction* cnull = graph_->GetNullConstant();
8480 HInstruction* c11 = graph_->GetIntConstant(11);
8481 HInstruction* param1 = MakeParam(DataType::Type::kBool);
8482 HInstruction* param2 = MakeParam(DataType::Type::kBool);
8483
8484 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
8485 HInstruction* cls = MakeClassLoad();
8486 HInstruction* new_inst = MakeNewInstance(cls);
8487 HInstruction* entry_write = MakeIFieldSet(new_inst, c11, MemberOffset(32));
8488 HInstruction* entry_if = new (GetAllocator()) HIf(param1);
8489 entry->AddInstruction(suspend);
8490 entry->AddInstruction(cls);
8491 entry->AddInstruction(new_inst);
8492 entry->AddInstruction(entry_write);
8493 entry->AddInstruction(entry_if);
8494 ManuallyBuildEnvFor(suspend, {});
8495 ManuallyBuildEnvFor(cls, {});
8496 ManuallyBuildEnvFor(new_inst, {});
8497
8498 HInstruction* left1_call = MakeInvoke(DataType::Type::kVoid, { new_inst });
8499 HInstruction* left1_goto = new (GetAllocator()) HGoto();
8500 left1->AddInstruction(left1_call);
8501 left1->AddInstruction(left1_goto);
8502 ManuallyBuildEnvFor(left1_call, {});
8503
8504 HInstruction* right1_goto = new (GetAllocator()) HGoto();
8505 right1->AddInstruction(right1_goto);
8506
8507 HInstruction* middle_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
8508 HInstruction* middle_if = new (GetAllocator()) HIf(param2);
8509 if (GetParam() == UsesOrder::kDefaultOrder) {
8510 middle->AddInstruction(middle_read);
8511 }
8512 middle->AddInstruction(middle_if);
8513
8514 HInstruction* left2_call = MakeInvoke(DataType::Type::kVoid, {});
8515 HInstruction* left2_goto = new (GetAllocator()) HGoto();
8516 left2->AddInstruction(left2_call);
8517 left2->AddInstruction(left2_goto);
8518 ManuallyBuildEnvFor(left2_call, {});
8519
8520 HInstruction* right2_goto = new (GetAllocator()) HGoto();
8521 right2->AddInstruction(right2_goto);
8522
8523 HInstruction* breturn_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
8524 HInstruction* breturn_add =
8525 new (GetAllocator()) HAdd(DataType::Type::kInt32, middle_read, breturn_read);
8526 HInstruction* breturn_return = new (GetAllocator()) HReturn(breturn_add);
8527 breturn->AddInstruction(breturn_read);
8528 breturn->AddInstruction(breturn_add);
8529 breturn->AddInstruction(breturn_return);
8530
8531 if (GetParam() == UsesOrder::kReverseOrder) {
8532 // Insert `middle_read` in the same position as for the `kDefaultOrder` case.
8533 // The only difference is the order of entries in `new_inst->GetUses()` which
8534 // is used by `HeapReferenceData::CollectReplacements()` and defines the order
8535 // of instructions to process for `HeapReferenceData::PredicateInstructions()`.
8536 middle->InsertInstructionBefore(middle_read, middle_if);
8537 }
8538
8539 SetupExit(exit);
8540
8541 // PerformLSE expects this to be empty.
8542 graph_->ClearDominanceInformation();
8543 LOG(INFO) << "Pre LSE " << blks;
8544 PerformLSE();
8545 LOG(INFO) << "Post LSE " << blks;
8546
8547 EXPECT_INS_RETAINED(cls);
8548 EXPECT_INS_REMOVED(new_inst);
8549 HNewInstance* replacement_new_inst = FindSingleInstruction<HNewInstance>(graph_);
8550 ASSERT_NE(replacement_new_inst, nullptr);
8551 EXPECT_INS_REMOVED(entry_write);
8552 HInstanceFieldSet* replacement_write = FindSingleInstruction<HInstanceFieldSet>(graph_);
8553 ASSERT_NE(replacement_write, nullptr);
8554 ASSERT_FALSE(replacement_write->GetIsPredicatedSet());
8555 ASSERT_INS_EQ(replacement_write->InputAt(0), replacement_new_inst);
8556 ASSERT_INS_EQ(replacement_write->InputAt(1), c11);
8557
8558 EXPECT_INS_RETAINED(left1_call);
8559
8560 EXPECT_INS_REMOVED(middle_read);
8561 HPredicatedInstanceFieldGet* replacement_middle_read =
8562 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, middle);
8563 ASSERT_NE(replacement_middle_read, nullptr);
8564 ASSERT_TRUE(replacement_middle_read->GetTarget()->IsPhi());
8565 ASSERT_EQ(2u, replacement_middle_read->GetTarget()->AsPhi()->InputCount());
8566 ASSERT_INS_EQ(replacement_middle_read->GetTarget()->AsPhi()->InputAt(0), replacement_new_inst);
8567 ASSERT_INS_EQ(replacement_middle_read->GetTarget()->AsPhi()->InputAt(1), cnull);
8568 ASSERT_TRUE(replacement_middle_read->GetDefaultValue()->IsPhi());
8569 ASSERT_EQ(2u, replacement_middle_read->GetDefaultValue()->AsPhi()->InputCount());
8570 ASSERT_INS_EQ(replacement_middle_read->GetDefaultValue()->AsPhi()->InputAt(0), c0);
8571 ASSERT_INS_EQ(replacement_middle_read->GetDefaultValue()->AsPhi()->InputAt(1), c11);
8572
8573 EXPECT_INS_RETAINED(left2_call);
8574
8575 EXPECT_INS_REMOVED(breturn_read);
8576 HPredicatedInstanceFieldGet* replacement_breturn_read =
8577 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
8578 ASSERT_NE(replacement_breturn_read, nullptr);
8579 ASSERT_INS_EQ(replacement_breturn_read->GetTarget(), replacement_middle_read->GetTarget());
8580 ASSERT_INS_EQ(replacement_breturn_read->GetDefaultValue(), replacement_middle_read);
8581 }
8582
8583 INSTANTIATE_TEST_SUITE_P(LoadStoreEliminationTest,
8584 UsesOrderDependentTestGroup,
8585 testing::Values(UsesOrder::kDefaultOrder, UsesOrder::kReverseOrder));
8586
8587 // The parameter is the number of times we call `std::next_permutation` (from 0 to 5)
8588 // so that we test all 6 permutation of three items.
8589 class UsesOrderDependentTestGroupForThreeItems
8590 : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<size_t>> {};
8591
8592 // Make sure that after we record replacements by predicated loads, we correctly
8593 // use that predicated load for Phi placeholders that were previously marked as
8594 // replaced by the now removed unpredicated load. (The fix for bug 183897743 was
8595 // not good enough.) Bug: 188188275
8596 // // ENTRY
8597 // obj = new Obj();
8598 // obj.foo = 11;
8599 // if (param1) {
8600 // // LEFT1
8601 // escape(obj);
8602 // } else {
8603 // // RIGHT1
8604 // }
8605 // // MIDDLE1
8606 // a = obj.foo;
8607 // if (param2) {
8608 // // LEFT2
8609 // no_escape1();
8610 // } else {
8611 // // RIGHT2
8612 // }
8613 // // MIDDLE2
8614 // if (param3) {
8615 // // LEFT3
8616 // x = obj.foo;
8617 // no_escape2();
8618 // } else {
8619 // // RIGHT3
8620 // x = 0;
8621 // }
8622 // // BRETURN
8623 // b = obj.foo;
8624 // return a + b + x;
TEST_P(UsesOrderDependentTestGroupForThreeItems,RecordPredicatedReplacements3)8625 TEST_P(UsesOrderDependentTestGroupForThreeItems, RecordPredicatedReplacements3) {
8626 ScopedObjectAccess soa(Thread::Current());
8627 VariableSizedHandleScope vshs(soa.Self());
8628 CreateGraph(&vshs);
8629 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
8630 "exit",
8631 {{"entry", "left1"},
8632 {"entry", "right1"},
8633 {"left1", "middle1"},
8634 {"right1", "middle1"},
8635 {"middle1", "left2"},
8636 {"middle1", "right2"},
8637 {"left2", "middle2"},
8638 {"right2", "middle2"},
8639 {"middle2", "left3"},
8640 {"middle2", "right3"},
8641 {"left3", "breturn"},
8642 {"right3", "breturn"},
8643 {"breturn", "exit"}}));
8644 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
8645 GET_BLOCK(entry);
8646 GET_BLOCK(left1);
8647 GET_BLOCK(right1);
8648 GET_BLOCK(middle1);
8649 GET_BLOCK(left2);
8650 GET_BLOCK(right2);
8651 GET_BLOCK(middle2);
8652 GET_BLOCK(left3);
8653 GET_BLOCK(right3);
8654 GET_BLOCK(breturn);
8655 GET_BLOCK(exit);
8656 #undef GET_BLOCK
8657 EnsurePredecessorOrder(middle1, {left1, right1});
8658 EnsurePredecessorOrder(middle2, {left2, right2});
8659 EnsurePredecessorOrder(breturn, {left3, right3});
8660 HInstruction* c0 = graph_->GetIntConstant(0);
8661 HInstruction* cnull = graph_->GetNullConstant();
8662 HInstruction* c11 = graph_->GetIntConstant(11);
8663 HInstruction* param1 = MakeParam(DataType::Type::kBool);
8664 HInstruction* param2 = MakeParam(DataType::Type::kBool);
8665 HInstruction* param3 = MakeParam(DataType::Type::kBool);
8666
8667 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
8668 HInstruction* cls = MakeClassLoad();
8669 HInstruction* new_inst = MakeNewInstance(cls);
8670 HInstruction* entry_write = MakeIFieldSet(new_inst, c11, MemberOffset(32));
8671 HInstruction* entry_if = new (GetAllocator()) HIf(param1);
8672 entry->AddInstruction(suspend);
8673 entry->AddInstruction(cls);
8674 entry->AddInstruction(new_inst);
8675 entry->AddInstruction(entry_write);
8676 entry->AddInstruction(entry_if);
8677 ManuallyBuildEnvFor(suspend, {});
8678 ManuallyBuildEnvFor(cls, {});
8679 ManuallyBuildEnvFor(new_inst, {});
8680
8681 HInstruction* left1_call = MakeInvoke(DataType::Type::kVoid, { new_inst });
8682 HInstruction* left1_goto = new (GetAllocator()) HGoto();
8683 left1->AddInstruction(left1_call);
8684 left1->AddInstruction(left1_goto);
8685 ManuallyBuildEnvFor(left1_call, {});
8686
8687 HInstruction* right1_goto = new (GetAllocator()) HGoto();
8688 right1->AddInstruction(right1_goto);
8689
8690 HInstruction* middle1_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
8691 HInstruction* middle1_if = new (GetAllocator()) HIf(param2);
8692 // Delay inserting `middle1_read`, do that later with ordering based on `GetParam()`.
8693 middle1->AddInstruction(middle1_if);
8694
8695 HInstruction* left2_call = MakeInvoke(DataType::Type::kVoid, {});
8696 HInstruction* left2_goto = new (GetAllocator()) HGoto();
8697 left2->AddInstruction(left2_call);
8698 left2->AddInstruction(left2_goto);
8699 ManuallyBuildEnvFor(left2_call, {});
8700
8701 HInstruction* right2_goto = new (GetAllocator()) HGoto();
8702 right2->AddInstruction(right2_goto);
8703
8704 HInstruction* middle2_if = new (GetAllocator()) HIf(param3);
8705 middle2->AddInstruction(middle2_if);
8706
8707 HInstruction* left3_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
8708 HInstruction* left3_call = MakeInvoke(DataType::Type::kVoid, {});
8709 HInstruction* left3_goto = new (GetAllocator()) HGoto();
8710 // Delay inserting `left3_read`, do that later with ordering based on `GetParam()`.
8711 left3->AddInstruction(left3_call);
8712 left3->AddInstruction(left3_goto);
8713 ManuallyBuildEnvFor(left3_call, {});
8714
8715 HInstruction* right3_goto = new (GetAllocator()) HGoto();
8716 right3->AddInstruction(right3_goto);
8717
8718 HPhi* breturn_phi = MakePhi({left3_read, c0});
8719 HInstruction* breturn_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
8720 HInstruction* breturn_add1 =
8721 new (GetAllocator()) HAdd(DataType::Type::kInt32, middle1_read, breturn_read);
8722 HInstruction* breturn_add2 =
8723 new (GetAllocator()) HAdd(DataType::Type::kInt32, breturn_add1, breturn_phi);
8724 HInstruction* breturn_return = new (GetAllocator()) HReturn(breturn_add2);
8725 breturn->AddPhi(breturn_phi);
8726 // Delay inserting `breturn_read`, do that later with ordering based on `GetParam()`.
8727 breturn->AddInstruction(breturn_add1);
8728 breturn->AddInstruction(breturn_add2);
8729 breturn->AddInstruction(breturn_return);
8730
8731 // Insert reads in the same positions but in different insertion orders.
8732 // The only difference is the order of entries in `new_inst->GetUses()` which
8733 // is used by `HeapReferenceData::CollectReplacements()` and defines the order
8734 // of instructions to process for `HeapReferenceData::PredicateInstructions()`.
8735 std::tuple<size_t, HInstruction*, HInstruction*> read_insertions[] = {
8736 { 0u, middle1_read, middle1_if },
8737 { 1u, left3_read, left3_call },
8738 { 2u, breturn_read, breturn_add1 },
8739 };
8740 for (size_t i = 0, num = GetParam(); i != num; ++i) {
8741 std::next_permutation(read_insertions, read_insertions + std::size(read_insertions));
8742 }
8743 for (auto [order, read, cursor] : read_insertions) {
8744 cursor->GetBlock()->InsertInstructionBefore(read, cursor);
8745 }
8746
8747 SetupExit(exit);
8748
8749 // PerformLSE expects this to be empty.
8750 graph_->ClearDominanceInformation();
8751 LOG(INFO) << "Pre LSE " << blks;
8752 PerformLSE();
8753 LOG(INFO) << "Post LSE " << blks;
8754
8755 EXPECT_INS_RETAINED(cls);
8756 EXPECT_INS_REMOVED(new_inst);
8757 HNewInstance* replacement_new_inst = FindSingleInstruction<HNewInstance>(graph_);
8758 ASSERT_NE(replacement_new_inst, nullptr);
8759 EXPECT_INS_REMOVED(entry_write);
8760 HInstanceFieldSet* replacement_write = FindSingleInstruction<HInstanceFieldSet>(graph_);
8761 ASSERT_NE(replacement_write, nullptr);
8762 ASSERT_FALSE(replacement_write->GetIsPredicatedSet());
8763 ASSERT_INS_EQ(replacement_write->InputAt(0), replacement_new_inst);
8764 ASSERT_INS_EQ(replacement_write->InputAt(1), c11);
8765
8766 EXPECT_INS_RETAINED(left1_call);
8767
8768 EXPECT_INS_REMOVED(middle1_read);
8769 HPredicatedInstanceFieldGet* replacement_middle1_read =
8770 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, middle1);
8771 ASSERT_NE(replacement_middle1_read, nullptr);
8772 ASSERT_TRUE(replacement_middle1_read->GetTarget()->IsPhi());
8773 ASSERT_EQ(2u, replacement_middle1_read->GetTarget()->AsPhi()->InputCount());
8774 ASSERT_INS_EQ(replacement_middle1_read->GetTarget()->AsPhi()->InputAt(0), replacement_new_inst);
8775 ASSERT_INS_EQ(replacement_middle1_read->GetTarget()->AsPhi()->InputAt(1), cnull);
8776 ASSERT_TRUE(replacement_middle1_read->GetDefaultValue()->IsPhi());
8777 ASSERT_EQ(2u, replacement_middle1_read->GetDefaultValue()->AsPhi()->InputCount());
8778 ASSERT_INS_EQ(replacement_middle1_read->GetDefaultValue()->AsPhi()->InputAt(0), c0);
8779 ASSERT_INS_EQ(replacement_middle1_read->GetDefaultValue()->AsPhi()->InputAt(1), c11);
8780
8781 EXPECT_INS_RETAINED(left2_call);
8782
8783 EXPECT_INS_REMOVED(left3_read);
8784 HPredicatedInstanceFieldGet* replacement_left3_read =
8785 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, left3);
8786 ASSERT_NE(replacement_left3_read, nullptr);
8787 ASSERT_TRUE(replacement_left3_read->GetTarget()->IsPhi());
8788 ASSERT_INS_EQ(replacement_left3_read->GetTarget(), replacement_middle1_read->GetTarget());
8789 ASSERT_INS_EQ(replacement_left3_read->GetDefaultValue(), replacement_middle1_read);
8790 EXPECT_INS_RETAINED(left3_call);
8791
8792 EXPECT_INS_RETAINED(breturn_phi);
8793 EXPECT_INS_REMOVED(breturn_read);
8794 HPredicatedInstanceFieldGet* replacement_breturn_read =
8795 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
8796 ASSERT_NE(replacement_breturn_read, nullptr);
8797 ASSERT_INS_EQ(replacement_breturn_read->GetTarget(), replacement_middle1_read->GetTarget());
8798 ASSERT_EQ(2u, replacement_breturn_read->GetDefaultValue()->AsPhi()->InputCount());
8799 ASSERT_INS_EQ(replacement_breturn_read->GetDefaultValue()->AsPhi()->InputAt(0),
8800 replacement_left3_read);
8801 ASSERT_INS_EQ(replacement_breturn_read->GetDefaultValue()->AsPhi()->InputAt(1),
8802 replacement_middle1_read);
8803 EXPECT_INS_RETAINED(breturn_add1);
8804 ASSERT_INS_EQ(breturn_add1->InputAt(0), replacement_middle1_read);
8805 ASSERT_INS_EQ(breturn_add1->InputAt(1), replacement_breturn_read);
8806 EXPECT_INS_RETAINED(breturn_add2);
8807 ASSERT_INS_EQ(breturn_add2->InputAt(0), breturn_add1);
8808 ASSERT_INS_EQ(breturn_add2->InputAt(1), breturn_phi);
8809 EXPECT_INS_RETAINED(breturn_return);
8810 }
8811
8812 INSTANTIATE_TEST_SUITE_P(LoadStoreEliminationTest,
8813 UsesOrderDependentTestGroupForThreeItems,
8814 testing::Values(0u, 1u, 2u, 3u, 4u, 5u));
8815
8816 } // namespace art
8817