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