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