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