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